From 4ed3570b5f4ad29efcfad82b81941007d23259c1 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 11 Jul 2024 00:22:48 -0400 Subject: [PATCH 01/11] Adding SyntaxContext Initial commit --- sources/SilkTouch/Clang/SyntaxContext.cs | 456 +++++++++++++++++++++++ sources/SilkTouch/SilkTouchGenerator.cs | 2 + 2 files changed, 458 insertions(+) create mode 100644 sources/SilkTouch/Clang/SyntaxContext.cs diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs new file mode 100644 index 0000000000..3f36758e24 --- /dev/null +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -0,0 +1,456 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using ClangSharp; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// +/// +public class SyntaxContext +{ + /// + /// + /// + /// + public SyntaxContext(GeneratedSyntax syntax) + { + foreach ((var fName, var node) in syntax.Files) + { + if (node is CompilationUnitSyntax comp) + { + Files.Add(fName, new CompilationContext(fName, comp, TypeDefinitionContainers)); + } + else + { + throw new Exception("CompilationUnitSyntax missing"); + } + } + } + + Dictionary Files = []; + + Dictionary Namespaces = []; + + Dictionary> TypeDefinitionContainers = []; + + private class CompilationContext + { + public CompilationContext(string file, CompilationUnitSyntax node, Dictionary> TypeDefinitionContainers) + { + List usings = node.Usings.Select(u => u.Name!.ToString()).ToList(); + foreach (var member in node.Members) + { + if (member is BaseNamespaceDeclarationSyntax ns) + { + var nsContext = new NamespaceContext(string.Empty, ns, TypeDefinitionContainers, usings, file); + Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); + } + else + { + throw new Exception($"CompilationUnit for {file} contains a member of type ({member.GetType()}) which isn't supported"); + } + } + Node = node.WithMembers(List(Array.Empty())); + } + + public CompilationUnitSyntax Node; + public Dictionary Namespaces = []; + + public CompilationUnitSyntax ToCompletedNode() + { + return Node.WithMembers(List(Namespaces.Select(n => (MemberDeclarationSyntax)n.Value.ToCompletedNode()))); + } + } + + private class NamespaceContext + { + public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Dictionary> TypeDefinitionContainers, List usings, string file = "") + { + string[] names = node.Name.ToString().Split('.'); + + namesp += $"{(namesp.Length > 0 ? "." : "")}{node.Name}"; + if (names.Length != 1) + { + NamespaceContext child = new NamespaceContext(namesp, node.WithName(IdentifierName(names[names.Length - 1])), TypeDefinitionContainers, usings, file); + namesp = namesp.Remove(namesp.Length - (names.Last().Length + 1)); + node = node.WithMembers(List(Array.Empty())); + + for (int i = names.Length - 2; i >= 1; i--) + { + NamespaceContext current = new NamespaceContext(namesp, node.WithName(IdentifierName(names[i])), TypeDefinitionContainers, usings, file); + namesp = namesp.Remove(namesp.Length - (names[i].Length + 1)); + current.Namespaces.Add(names[i + 1], child); + child = current; + } + + Namespaces.Add(names[1], child); + Node = node.WithName(IdentifierName(names[0])); + return; + } + + foreach(var member in node.Members) + { + if (member is BaseNamespaceDeclarationSyntax ns) + { + var nsContext = new NamespaceContext(namesp, ns, TypeDefinitionContainers, usings, file); + Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); + } + else if (member is EnumDeclarationSyntax e) + { + var en = new TypeContextContainer(namesp, new EnumContext(file, e)); + if (e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) + { + if (!TypeDefinitionContainers.TryGetValue(e.Identifier.Text, out var list)) + { + list = []; + TypeDefinitionContainers.Add(e.Identifier.Text, list); + } + if (list.Count == 1 && list[0].Type is UnknownTypeContext) + { + list[0].Type = en.Type; + list[0].Namespace = en.Namespace; + en = list[0]; + } + else + { + list.Add(en); + } + } + Types.Add(e.Identifier.Text, en); + } + else if (member is TypeDeclarationSyntax t) + { + var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, TypeDefinitionContainers, usings)); + if (t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) + { + if (!TypeDefinitionContainers.TryGetValue(t.Identifier.Text, out var list)) + { + list = []; + TypeDefinitionContainers.Add(t.Identifier.Text, list); + } + if (list.Count == 1 && list[0].Type is UnknownTypeContext) + { + list[0].Type = ty.Type; + list[0].Namespace = ty.Namespace; + ty = list[0]; + } + else + { + list.Add(ty); + } + } + Types.Add(t.Identifier.Text, ty); + } + else + { + throw new Exception($"Namespace {node.Name}{(file != "" ? " in file " + file : "")} contains a member of type ({member.GetType()}) which isn't supported"); + } + } + + Node = node.WithName(IdentifierName(names[0])); + } + + public BaseNamespaceDeclarationSyntax Node; + public Dictionary Namespaces = []; + public Dictionary Types = []; + + public BaseNamespaceDeclarationSyntax ToCompletedNode() + { + List members = []; + members.AddRange(Namespaces.Select(n => n.Value.ToCompletedNode())); + members.AddRange(Types.Select(t => t.Value.Type.ToCompletedNode()!)); + return Node.WithMembers(List(members)); + } + } + + private class TypeContext : IBaseTypeContext + { + public TypeContext(string ns, string file, TypeDeclarationSyntax node, Dictionary> TypeDefinitionContainers, List usings) + { + File = file; + + foreach (var member in node.Members) + { + if (member is EnumDeclarationSyntax e) + { + var en = new TypeContextContainer(ns, new EnumContext(file, e)); + if (e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) + { + if (!TypeDefinitionContainers.TryGetValue(e.Identifier.Text, out var list)) + { + list = []; + TypeDefinitionContainers.Add(e.Identifier.Text, list); + } + if (list.Count == 1 && list[0].Type is UnknownTypeContext) + { + list[0].Type = en.Type; + list[0].Namespace = en.Namespace; + en = list[0]; + } + else + { + list.Add(en); + } + } + SubTypes.Add(e.Identifier.Text, en); + } + else if (member is TypeDeclarationSyntax t) + { + var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, TypeDefinitionContainers, usings)); + if (t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) + { + if (!TypeDefinitionContainers.TryGetValue(t.Identifier.Text, out var list)) + { + list = []; + TypeDefinitionContainers.Add(t.Identifier.Text, list); + } + if (list.Count == 1 && list[0].Type is UnknownTypeContext) + { + list[0].Type = ty.Type; + list[0].Namespace = ty.Namespace; + ty = list[0]; + } + else + { + list.Add(ty); + } + } + SubTypes.Add(t.Identifier.Text, ty); + } + else if (member is MethodDeclarationSyntax m) + { + string name = m.Identifier.Text; + if (!Methods.TryGetValue(name, out var methods)) + { + methods = new(); + Methods.Add(name, methods); + } + methods.Add(new(m)); + } + else if (member is FieldDeclarationSyntax f) + { + TypeSyntax syn = f.Declaration.Type; + int pDepth = 0; + while (syn is PointerTypeSyntax pointer) + { + pDepth++; + syn = pointer.ElementType; + } + + TypeContextContainer type; + if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) + { + type = list.First(); + + foreach(var decl in list) + { + if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace)) + { + type = decl; + break; + } + } + } + else + { + type = new(string.Empty, new UnknownTypeContext(syn)); + TypeDefinitionContainers.Add(syn.ToString(), new() { type }); + } + + foreach (var dec in f.Declaration.Variables) + { + Fields.Add(dec.Identifier.Text, new(type, pDepth, f.WithDeclaration(f.Declaration.WithVariables(SeparatedList(new[] { dec }))))); + } + } + else if (member is PropertyDeclarationSyntax p) + { + TypeSyntax syn = p.Type; + int pDepth = 0; + while (syn is PointerTypeSyntax pointer) + { + pDepth++; + syn = pointer.ElementType; + } + + TypeContextContainer type; + if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) + { + type = list.First(); + + foreach (var decl in list) + { + if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace)) + { + type = decl; + break; + } + } + } + else + { + type = new(string.Empty, new UnknownTypeContext(syn)); + TypeDefinitionContainers.Add(syn.ToString(), new() { type }); + } + + Properties.Add(p.Identifier.Text, new(type, pDepth, p)); + } + } + + Node = node.WithMembers(List(Array.Empty())); + } + + public string File; + public TypeDeclarationSyntax Node; + public Dictionary SubTypes = []; + public Dictionary> Methods = []; + public Dictionary Fields = []; + public Dictionary Properties = []; + + public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); + + public MemberDeclarationSyntax? ToCompletedNode() + { + List members = []; + members.AddRange(SubTypes.Select(t => t.Value.Type.ToCompletedNode()!)); + foreach (string method in Methods.Keys) + { + members.AddRange(Methods[method].Select(m => m.Node)); + } + members.AddRange(Fields.Select(f => f.Value.ToCompletedNode())); + members.AddRange(Properties.Select(p => p.Value.ToCompletedNode())); + return Node.WithMembers(List(members)); + } + } + + private class EnumContext : LeafNodeContext, IBaseTypeContext + { + public EnumContext(string file, EnumDeclarationSyntax node) : base(node) { File = file; } + + public string File; + public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); + + public MemberDeclarationSyntax? ToCompletedNode() + { + return Node; + } + } + + private interface IBaseTypeContext + { + TypeSyntax Syntax { get; } + + MemberDeclarationSyntax? ToCompletedNode(); + } + + private class UnknownTypeContext : IBaseTypeContext + { + public UnknownTypeContext(TypeSyntax type) { Type = type; } + TypeSyntax Type; + + public TypeSyntax Syntax => Type; + + public MemberDeclarationSyntax? ToCompletedNode() + { + return null; + } + } + + private class TypeContextContainer + { + public TypeContextContainer(string ns, IBaseTypeContext ty) + { + Namespace = ns; + Type = ty; + } + + public string Namespace; + public IBaseTypeContext Type; + } + + private class MethodContext : LeafNodeContext + { + public MethodContext(MethodDeclarationSyntax node) : base(node) { } + } + + private class FieldContext : VariableNodes + { + public FieldContext(TypeContextContainer container, int pointerDepth, FieldDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + + public int PointerDepth; + + public FieldDeclarationSyntax ToCompletedNode() + { + var type = Container.Type.Syntax; + + int pDepth = PointerDepth; + while (pDepth > 0) + { + type = PointerType(type); + pDepth--; + } + + return Node.WithDeclaration(Node.Declaration.WithType(type)); + } + } + + private class PropertyContext : VariableNodes + { + public PropertyContext(TypeContextContainer container, int pointerDepth, PropertyDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + + public int PointerDepth; + + public PropertyDeclarationSyntax ToCompletedNode() + { + var type = Container.Type.Syntax; + + int pDepth = PointerDepth; + while (pDepth > 0) + { + type = PointerType(type); + pDepth--; + } + + return Node.WithType(type); + } + } + + private class VariableNodes : LeafNodeContext + { + public VariableNodes(TypeContextContainer container, TNodeType node) : base(node) { Container = container; } + + public TypeContextContainer Container; + } + + private class LeafNodeContext(TNodeType node) + { + public TNodeType Node = node; + } + + /// + /// checks if ns1 is a child namespace of ns2 + /// + /// + /// + /// + private static bool NamespaceMatch(string ns1, string ns2) + { + for (int i = 0; i < ns2.Length; i++) + { + if (ns2[i] != ns1[i]) + return false; + } + return true; + } +} diff --git a/sources/SilkTouch/SilkTouchGenerator.cs b/sources/SilkTouch/SilkTouchGenerator.cs index 27c345e951..2a91254e37 100644 --- a/sources/SilkTouch/SilkTouchGenerator.cs +++ b/sources/SilkTouch/SilkTouchGenerator.cs @@ -256,6 +256,8 @@ var file in await cacheProvider.GetFiles( rawBindings.Files.Clear(); // GC ASAP var bindings = new GeneratedSyntax(syntaxTrees, rawBindings.Diagnostics); + var context = new SyntaxContext( bindings ); + // Mod the bindings // ReSharper disable once LoopCanBeConvertedToQuery foreach (var mod in jobMods) From 04ca8c92a3d7f1c20dd3a34c271e034c5016e63e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 11 Jul 2024 09:37:58 -0400 Subject: [PATCH 02/11] Update Context Unified TypeContextContainer code Added Parameter tracking for methods --- sources/SilkTouch/Clang/SyntaxContext.cs | 266 +++++++++++------------ 1 file changed, 125 insertions(+), 141 deletions(-) diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 3f36758e24..3addf3792e 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -30,7 +30,7 @@ public SyntaxContext(GeneratedSyntax syntax) { if (node is CompilationUnitSyntax comp) { - Files.Add(fName, new CompilationContext(fName, comp, TypeDefinitionContainers)); + Files.Add(fName, new CompilationContext(fName, comp, this)); } else { @@ -45,16 +45,106 @@ public SyntaxContext(GeneratedSyntax syntax) Dictionary> TypeDefinitionContainers = []; + private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List usings, TypeContext? currentType, out int pDepth, string parentName = "") + { + pDepth = 0; + while (syn is PointerTypeSyntax pointer) + { + pDepth++; + syn = pointer.ElementType; + } + + string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{currentType?.Node.Identifier.Text ?? string.Empty}"; + + TypeContextContainer type; + if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) + { + type = list.First(); + + foreach (var decl in list) + { + if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace) && (decl.IsPublic)) + { + type = decl; + break; + } + } + } + else if (GetChildContainer(syn, currentType, out list, name) && list is not null) + { + type = list.First(); + + foreach (var decl in list) + { + if (ns == decl.Namespace) + { + type = decl; + break; + } + } + } + else + { + type = new(string.Empty, new UnknownTypeContext(syn), true); + TypeDefinitionContainers.Add(syn.ToString(), new() { type }); + } + + return type; + } + + private void AddTypeContextContainer(ref TypeContextContainer container, string name) + { + if (!TypeDefinitionContainers.TryGetValue(name, out var list)) + { + list = []; + TypeDefinitionContainers.Add(name, list); + } + if (list.Count == 1 && list[0].Type is UnknownTypeContext) + { + list[0].Type = container.Type; + list[0].Namespace = container.Namespace; + container = list[0]; + } + else + { + list.Add(container); + } + } + + private bool GetChildContainer(TypeSyntax syn, TypeContext? currentType, out List? list, string parentName, bool topLevel = true) + { + list = null; + if (currentType is null) + { + return false; + } + + foreach(var child in currentType.SubTypes) + { + string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{child.Key}"; + if (TypeDefinitionContainers.TryGetValue(name, out list)) + { + return true; + } + + if (GetChildContainer(syn, child.Value.Type as TypeContext, out list, name, false)) + { + return true; + } + } + return false; + } + private class CompilationContext { - public CompilationContext(string file, CompilationUnitSyntax node, Dictionary> TypeDefinitionContainers) + public CompilationContext(string file, CompilationUnitSyntax node, SyntaxContext context) { List usings = node.Usings.Select(u => u.Name!.ToString()).ToList(); foreach (var member in node.Members) { if (member is BaseNamespaceDeclarationSyntax ns) { - var nsContext = new NamespaceContext(string.Empty, ns, TypeDefinitionContainers, usings, file); + var nsContext = new NamespaceContext(string.Empty, ns, context, usings, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else @@ -76,20 +166,20 @@ public CompilationUnitSyntax ToCompletedNode() private class NamespaceContext { - public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Dictionary> TypeDefinitionContainers, List usings, string file = "") + public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, SyntaxContext context, List usings, string file = "") { string[] names = node.Name.ToString().Split('.'); namesp += $"{(namesp.Length > 0 ? "." : "")}{node.Name}"; if (names.Length != 1) { - NamespaceContext child = new NamespaceContext(namesp, node.WithName(IdentifierName(names[names.Length - 1])), TypeDefinitionContainers, usings, file); + NamespaceContext child = new NamespaceContext(namesp, node.WithName(IdentifierName(names[names.Length - 1])), context, usings, file); namesp = namesp.Remove(namesp.Length - (names.Last().Length + 1)); node = node.WithMembers(List(Array.Empty())); for (int i = names.Length - 2; i >= 1; i--) { - NamespaceContext current = new NamespaceContext(namesp, node.WithName(IdentifierName(names[i])), TypeDefinitionContainers, usings, file); + NamespaceContext current = new NamespaceContext(namesp, node.WithName(IdentifierName(names[i])), context, usings, file); namesp = namesp.Remove(namesp.Length - (names[i].Length + 1)); current.Namespaces.Add(names[i + 1], child); child = current; @@ -104,53 +194,19 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Dict { if (member is BaseNamespaceDeclarationSyntax ns) { - var nsContext = new NamespaceContext(namesp, ns, TypeDefinitionContainers, usings, file); + var nsContext = new NamespaceContext(namesp, ns, context, usings, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else if (member is EnumDeclarationSyntax e) { - var en = new TypeContextContainer(namesp, new EnumContext(file, e)); - if (e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) - { - if (!TypeDefinitionContainers.TryGetValue(e.Identifier.Text, out var list)) - { - list = []; - TypeDefinitionContainers.Add(e.Identifier.Text, list); - } - if (list.Count == 1 && list[0].Type is UnknownTypeContext) - { - list[0].Type = en.Type; - list[0].Namespace = en.Namespace; - en = list[0]; - } - else - { - list.Add(en); - } - } + var en = new TypeContextContainer(namesp, new EnumContext(file, e), e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + context.AddTypeContextContainer(ref en, e.Identifier.Text); Types.Add(e.Identifier.Text, en); } else if (member is TypeDeclarationSyntax t) { - var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, TypeDefinitionContainers, usings)); - if (t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) - { - if (!TypeDefinitionContainers.TryGetValue(t.Identifier.Text, out var list)) - { - list = []; - TypeDefinitionContainers.Add(t.Identifier.Text, list); - } - if (list.Count == 1 && list[0].Type is UnknownTypeContext) - { - list[0].Type = ty.Type; - list[0].Namespace = ty.Namespace; - ty = list[0]; - } - else - { - list.Add(ty); - } - } + var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, context, usings), t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + context.AddTypeContextContainer(ref ty, t.Identifier.Text); Types.Add(t.Identifier.Text, ty); } else @@ -177,7 +233,7 @@ public BaseNamespaceDeclarationSyntax ToCompletedNode() private class TypeContext : IBaseTypeContext { - public TypeContext(string ns, string file, TypeDeclarationSyntax node, Dictionary> TypeDefinitionContainers, List usings) + public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxContext context, List usings, string parentName = "") { File = file; @@ -185,48 +241,16 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, Dictionar { if (member is EnumDeclarationSyntax e) { - var en = new TypeContextContainer(ns, new EnumContext(file, e)); - if (e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) - { - if (!TypeDefinitionContainers.TryGetValue(e.Identifier.Text, out var list)) - { - list = []; - TypeDefinitionContainers.Add(e.Identifier.Text, list); - } - if (list.Count == 1 && list[0].Type is UnknownTypeContext) - { - list[0].Type = en.Type; - list[0].Namespace = en.Namespace; - en = list[0]; - } - else - { - list.Add(en); - } - } + string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{e.Identifier.Text}"; + var en = new TypeContextContainer(ns, new EnumContext(file, e), e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + context.AddTypeContextContainer(ref en, name); SubTypes.Add(e.Identifier.Text, en); } else if (member is TypeDeclarationSyntax t) { - var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, TypeDefinitionContainers, usings)); - if (t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))) - { - if (!TypeDefinitionContainers.TryGetValue(t.Identifier.Text, out var list)) - { - list = []; - TypeDefinitionContainers.Add(t.Identifier.Text, list); - } - if (list.Count == 1 && list[0].Type is UnknownTypeContext) - { - list[0].Type = ty.Type; - list[0].Namespace = ty.Namespace; - ty = list[0]; - } - else - { - list.Add(ty); - } - } + string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{t.Identifier.Text}"; + var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, context, usings, name), t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + context.AddTypeContextContainer(ref ty, name); SubTypes.Add(t.Identifier.Text, ty); } else if (member is MethodDeclarationSyntax m) @@ -237,38 +261,12 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, Dictionar methods = new(); Methods.Add(name, methods); } - methods.Add(new(m)); + methods.Add(new(ns, m, context, usings, parentName, this)); } else if (member is FieldDeclarationSyntax f) { - TypeSyntax syn = f.Declaration.Type; - int pDepth = 0; - while (syn is PointerTypeSyntax pointer) - { - pDepth++; - syn = pointer.ElementType; - } + TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); - TypeContextContainer type; - if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) - { - type = list.First(); - - foreach(var decl in list) - { - if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace)) - { - type = decl; - break; - } - } - } - else - { - type = new(string.Empty, new UnknownTypeContext(syn)); - TypeDefinitionContainers.Add(syn.ToString(), new() { type }); - } - foreach (var dec in f.Declaration.Variables) { Fields.Add(dec.Identifier.Text, new(type, pDepth, f.WithDeclaration(f.Declaration.WithVariables(SeparatedList(new[] { dec }))))); @@ -276,33 +274,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, Dictionar } else if (member is PropertyDeclarationSyntax p) { - TypeSyntax syn = p.Type; - int pDepth = 0; - while (syn is PointerTypeSyntax pointer) - { - pDepth++; - syn = pointer.ElementType; - } - - TypeContextContainer type; - if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) - { - type = list.First(); - - foreach (var decl in list) - { - if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace)) - { - type = decl; - break; - } - } - } - else - { - type = new(string.Empty, new UnknownTypeContext(syn)); - TypeDefinitionContainers.Add(syn.ToString(), new() { type }); - } + TypeContextContainer type = context.GetTypeContainer(p.Type, ns, usings, this, out int pDepth, parentName); Properties.Add(p.Identifier.Text, new(type, pDepth, p)); } @@ -369,19 +341,31 @@ private class UnknownTypeContext : IBaseTypeContext private class TypeContextContainer { - public TypeContextContainer(string ns, IBaseTypeContext ty) + public TypeContextContainer(string ns, IBaseTypeContext ty, bool isPublic) { Namespace = ns; Type = ty; + IsPublic = isPublic; } public string Namespace; + public bool IsPublic; public IBaseTypeContext Type; } private class MethodContext : LeafNodeContext { - public MethodContext(MethodDeclarationSyntax node) : base(node) { } + public MethodContext(string ns, MethodDeclarationSyntax node, SyntaxContext context, List usings, string parentName, TypeContext type) : base(node) + { + foreach (var para in node.ParameterList.Parameters) + { + TypeContextContainer pType = context.GetTypeContainer(para.Type!, ns, usings, type, out int pDepth, parentName); + + Parameters.Add(para.Identifier.Text, pType); + } + } + + public Dictionary Parameters = []; } private class FieldContext : VariableNodes From 1e2df163e21caf59b51170c4071afc6185ccc476 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 11 Jul 2024 23:53:34 -0400 Subject: [PATCH 03/11] Add CommonType Merge -added some BaseType tracking -added member tracking in Enums for easier combining -added support for generic types --- sources/SilkTouch/Clang/SyntaxContext.cs | 787 +++++++++++++++++++++-- 1 file changed, 742 insertions(+), 45 deletions(-) diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 3addf3792e..eaf3ba616c 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -11,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Silk.NET.Core; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Clang; @@ -26,6 +29,7 @@ public class SyntaxContext /// public SyntaxContext(GeneratedSyntax syntax) { + //Build initial per file tree foreach ((var fName, var node) in syntax.Files) { if (node is CompilationUnitSyntax comp) @@ -37,14 +41,421 @@ public SyntaxContext(GeneratedSyntax syntax) throw new Exception("CompilationUnitSyntax missing"); } } + + //Merge common types into single types + foreach(var typeDef in TypeDefinitionContainers) + { + if (typeDef.Value.Count <= 1) + { + continue; + } + + for (int i = 0; i < typeDef.Value.Count; i++) + { + List typesToMerge = []; + TypeContextContainer main = typeDef.Value[i]; + string mainFile = typeDef.Value.Where(t => t.Type?.FileName.Length > 0).First().Type!.FileName; + List files = [mainFile]; + + //find all types that are the same as our current main type + //this includes same generic arguments with the same constraints + //We also keep track of all file paths along the way + for (int j = 1; j < typeDef.Value.Count; j++) + { + if (main.Namespace != typeDef.Value[j].Namespace || + !TypeVarCheck(main, typeDef.Value[j])) + { + continue; + } + + string file = typeDef.Value[j].Type?.FileName ?? string.Empty; + if (file.Length > 0 && !files.Contains(file)) + { + files.Add(file); + } + + typesToMerge.Add(typeDef.Value[j]); + typeDef.Value.RemoveAt(j); + j--; + } + + if (typesToMerge.Count == 0) + { + continue; + } + + //Merge all types into main + MergeTypeContainers(main, typesToMerge); + + //get new path which is placed at the common root of all the paths + mainFile = Path.Combine(FindCommonRoot(files), Path.GetFileName(mainFile)).Replace(".Manual.cs", ".gen.cs"); + + int fileIndex = 1; + + //check that our new path doesn't already exist (unless it is our first file anyways) + while (Files.ContainsKey(mainFile) && !(files[0] == mainFile)) + { + mainFile = mainFile.Replace($"{(fileIndex > 1 ? fileIndex.ToString() : "")}.gen.cs", $"{fileIndex + 1}.gen.cs"); + fileIndex++; + } + + //if our file isn't the same as our old one add the new path and remove the original + if (files[0] != mainFile) + { + Files.Add(mainFile, Files[files[0]]); + Files.Remove(files[0]); + } + + //remove the first file path to avoid confusion + files.RemoveAt(0); + + NamespaceContext ns = Files[mainFile].GetNamespace(main.Namespace); + + //make sure our type actually is in our namespace (can happen if first type in declarations was an UnknownTypeContext which shouldn't happen) + if (!ns.Types.TryGetValue(typeDef.Key, out var list)) + { + list = [main]; + ns.Types.Add(typeDef.Key, list); + } + else if (!list.Contains(main)) + { + for (int j = 0; j < list.Count; j++) + { + if (main.Namespace != list[j].Namespace || + !TypeVarCheck(main, list[j])) + { + continue; + } + list.RemoveAt(j); + j--; + } + + list.Add(main); + } + + //finally check all files have types still and either delete them or clean them of bad types + foreach(var file in files) + { + if (Files.TryGetValue(file, out var comp) && comp.DefinedTypeCount == 0) + { + Files.Remove(file); + } + else if (comp is not null) + { + comp.Clean(); + } + } + } + } + + //TODO: Create Full Syntax Tree from individual file trees } Dictionary Files = []; - Dictionary Namespaces = []; + Dictionary SyntaxTree = []; Dictionary> TypeDefinitionContainers = []; + private string FindCommonRoot(List paths) + { + if (paths == null || paths.Count == 0) + return string.Empty; + + if (paths.Count == 1) + return paths[0]; + + string[][] separatedPaths = paths + .Select(path => path.Split(Path.DirectorySeparatorChar)) + .ToArray(); + + string commonPath = string.Join(Path.DirectorySeparatorChar.ToString(), + separatedPaths.First().TakeWhile((part, index) => + separatedPaths.All(path => path.Length > index && path[index] == part))); + + return commonPath; + } + + private bool TypeVarCheck(TypeContextContainer t1, TypeContextContainer t2) + { + if (t1.Type is EnumContext || t2.Type is EnumContext || + t1.Type is not TypeContext ty1 || t2.Type is not TypeContext ty2) + { + return true; + } + + if (ty1.Node.TypeParameterList is not null && ty2.Node.TypeParameterList is not null && + ty1.Node.TypeParameterList.Parameters.Count != ty2.Node.TypeParameterList.Parameters.Count || + (ty1.Node.ParameterList is null ^ ty2.Node.ParameterList is null)) + { + return false; + } + + return true; + } + + private bool TypeVarCheck(TypeContextContainer type, TypeSyntax syntax) + { + if (syntax is not GenericNameSyntax generic) + { + if (type.Type is EnumContext || (type.Type is TypeContext ty && + (ty.Node.TypeParameterList is null || ty.Node.TypeParameterList.Parameters.Count == 0)) || + (type.Type is UnknownTypeContext && type.Type.Syntax is not GenericNameSyntax)) + { + return true; + } + return false; + } + if (type.Type is EnumContext) + { + return false; + } + + if (type.Type is TypeContext ty1) + { + if ((ty1.Node.TypeParameterList is not null && + ty1.Node.TypeParameterList.Parameters.Count != generic.TypeArgumentList.Arguments.Count) || + ty1.Node.ParameterList is null) + { + return false; + } + + return true; + } + else if (type.Type is UnknownTypeContext && type.Type.Syntax is GenericNameSyntax generic2) + { + return generic.TypeArgumentList.Arguments.Count == generic2.TypeArgumentList.Arguments.Count; + } + + return false; + + } + + private void MergeTypeContainers(TypeContextContainer main, List typesToMerge) + { + while (main.Type is UnknownTypeContext u) + { + main.Type = typesToMerge[0].Type; + main.Visibility = typesToMerge[0].Visibility; + typesToMerge.RemoveAt(0); + } + + SyntaxKind visibility = main.IsPublic ? SyntaxKind.PublicKeyword : SyntaxKind.PrivateKeyword; + bool isStatic = true; + + if (main.Type is EnumContext e) + { + if (!e.Node.Modifiers.Any(mod => mod.IsKind(SyntaxKind.StaticKeyword))) + { + isStatic = false; + } + + bool emptyBaseType = e.Node.BaseList is null || e.Node.BaseList.Types.Count == 0; + + foreach (var merge in typesToMerge) + { + if (visibility != SyntaxKind.PublicKeyword) + { + if (merge.IsPublic) + { + visibility = SyntaxKind.PublicKeyword; + } + else if (visibility != SyntaxKind.ProtectedKeyword && merge.Visibility == SyntaxKind.ProtectedKeyword) + { + visibility = SyntaxKind.ProtectedKeyword; + } + } + + if (merge.Type is EnumContext en) + { + if (isStatic && !en.Node.Modifiers.Any(mod => mod.IsKind(SyntaxKind.StaticKeyword))) + { + isStatic = false; + } + + foreach (var member in en.Members) + { + if (e.Members.ContainsKey(member.Key)) + { + continue; + } + e.Members.Add(member.Key, member.Value); + } + + e.Node = e.Node.AddModifiers(en.Node.Modifiers.Where(mod => !mod.IsKind(SyntaxKind.PrivateKeyword) && + !mod.IsKind(SyntaxKind.ProtectedKeyword) && + !mod.IsKind(SyntaxKind.PublicKeyword) && + !e.Node.Modifiers.Any(eMod => mod.IsKind(eMod.Kind()))).ToArray()) + .AddAttributeLists(en.Node.AttributeLists.Select(al => + AttributeList(SeparatedList(al.Attributes.Where(at => !e.Node.AttributeLists + .Any(eAl => eAl.Attributes + .Any(eAt => at.ToString() != eAt.ToString())))))).ToArray()); + + if (emptyBaseType) + { + e.Node = e.Node.WithBaseList(en.Node.BaseList); + emptyBaseType = false; + } + } + else if(merge.Type is not UnknownTypeContext) + { + throw new Exception($"{main.Namespace}.{e.Node.Identifier.Text} is defined multiple times as both an enum and another type"); + } + merge.Type = null; + } + } + else if (main.Type is TypeContext t) + { + if (!t.Node.Modifiers.Any(mod => mod.IsKind(SyntaxKind.StaticKeyword))) + { + isStatic = false; + } + + bool hasBaseClass = !IsInterface(t.BaseTypes.Values.First()); + + foreach (var merge in typesToMerge) + { + if (visibility != SyntaxKind.PublicKeyword) + { + if (merge.IsPublic) + { + visibility = SyntaxKind.PublicKeyword; + } + else if (visibility != SyntaxKind.ProtectedKeyword && merge.Visibility == SyntaxKind.ProtectedKeyword) + { + visibility = SyntaxKind.ProtectedKeyword; + } + } + + if (merge.Type is TypeContext ty) + { + if (isStatic && !ty.Node.Modifiers.Any(mod => mod.IsKind(SyntaxKind.StaticKeyword))) + { + isStatic = false; + } + + foreach (var member in ty.Methods) + { + if (!t.Methods.TryGetValue(member.Key, out var list)) + { + t.Methods.Add(member.Key, member.Value); + continue; + } + + foreach (var method in member.Value) + { + if (list.Any(mem => method == mem)) + { + continue; + } + + list.Add(method); + } + } + + bool first = true; + foreach (var bType in ty.BaseTypes) + { + bool isInterface = IsInterface(bType.Value); + if (t.BaseTypes.ContainsKey(bType.Key) && ((first && !hasBaseClass) || isInterface)) + { + first = false; + continue; + } + + first = false; + if (isInterface) + { + t.BaseTypes.Add(bType.Key, bType.Value); + continue; + } + + t.BaseTypes = new Dictionary { [bType.Key] = bType.Value }.Concat(t.BaseTypes).ToDictionary(); + } + + t.Node = t.Node.WithBaseList(BaseList(SeparatedList(t.BaseTypes.Select(bType => (BaseTypeSyntax)SimpleBaseType(bType.Value.Type!.Syntax))))); + + foreach(var member in ty.Fields) + { + if (t.Fields.ContainsKey(member.Key)) + { + continue; + } + t.Fields.Add(member.Key, member.Value); + } + + foreach (var member in ty.Properties) + { + if (t.Properties.ContainsKey(member.Key)) + { + continue; + } + t.Properties.Add(member.Key, member.Value); + } + + foreach (var member in ty.SubTypes) + { + if (!t.SubTypes.TryGetValue(member.Key, out var list)) + { + t.SubTypes.Add(member.Key, member.Value); + continue; + } + + for (int i = 0; i < list.Count; i++) + { + List typesToMerge2 = []; + for (int j = 1; j < member.Value.Count; j++) + { + if (list[i].Namespace != member.Value[j].Namespace || + !TypeVarCheck(list[i], member.Value[j])) + { + continue; + } + typesToMerge2.Add(member.Value[j]); + member.Value.RemoveAt(j); + j--; + } + + if (typesToMerge2.Count == 0) + { + continue; + } + + MergeTypeContainers(list[i], typesToMerge2); + } + } + + + t.Node = t.Node.AddModifiers(ty.Node.Modifiers.Where(mod => !mod.IsKind(SyntaxKind.PrivateKeyword) && + !mod.IsKind(SyntaxKind.ProtectedKeyword) && + !mod.IsKind(SyntaxKind.PublicKeyword) && + !t.Node.Modifiers.Any(tMod => mod.IsKind(tMod.Kind()))).ToArray()) + .AddAttributeLists(ty.Node.AttributeLists.Select(al => + AttributeList(SeparatedList(al.Attributes.Where(at => !t.Node.AttributeLists + .Any(tAl => tAl.Attributes + .Any(tAt => at.ToString() != tAt.ToString())))))).ToArray()); + + } + else if (merge.Type is not UnknownTypeContext) + { + throw new Exception($"{main.Namespace}.{t.Node.Identifier.Text} is defined multiple times as both an type and an enum"); + } + merge.Type = null; + } + } + + } + + private bool IsInterface(TypeContextContainer container) + { + if (container.Type is not TypeContext t) + { + return false; + } + + return t.Node.IsKind(SyntaxKind.InterfaceDeclaration); + } + private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List usings, TypeContext? currentType, out int pDepth, string parentName = "") { pDepth = 0; @@ -56,14 +467,14 @@ private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List 0 ? $"{parentName}." : "")}{currentType?.Node.Identifier.Text ?? string.Empty}"; - TypeContextContainer type; - if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out var list)) + TypeContextContainer? type = null; + List? list; + if (TypeDefinitionContainers.TryGetValue(syn.ToString(), out list)) { - type = list.First(); foreach (var decl in list) { - if (NamespaceMatch(ns, decl.Namespace) || usings.Contains(decl.Namespace) && (decl.IsPublic)) + if ((NamespaceMatch(ns, decl.Namespace) || (usings.Contains(decl.Namespace) && decl.IsPublic)) && TypeVarCheck(decl, syn)) { type = decl; break; @@ -76,39 +487,51 @@ private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List? list, string parentName, bool topLevel = true) @@ -127,9 +550,12 @@ private bool GetChildContainer(TypeSyntax syn, TypeContext? currentType, out Lis return true; } - if (GetChildContainer(syn, child.Value.Type as TypeContext, out list, name, false)) + foreach (var ch in child.Value) { - return true; + if (GetChildContainer(syn, ch.Type as TypeContext, out list, name, false)) + { + return true; + } } } return false; @@ -162,6 +588,47 @@ public CompilationUnitSyntax ToCompletedNode() { return Node.WithMembers(List(Namespaces.Select(n => (MemberDeclarationSyntax)n.Value.ToCompletedNode()))); } + + public NamespaceContext GetNamespace(string ns) + { + int firstIndex = ns.IndexOf('.'); + if (firstIndex == -1) + { + firstIndex = ns.Length - 1; + } + + string name = ns.Substring(0, firstIndex); + if (!Namespaces.TryGetValue(name, out NamespaceContext? nameSpace)) + { + nameSpace = new NamespaceContext(NamespaceDeclaration(IdentifierName(name))); + Namespaces.Add(name, nameSpace); + } + return nameSpace.GetNamespace(ns.Substring(firstIndex + 1)); + } + + public int DefinedTypeCount => Namespaces.Select(n => n.Value.DefinedTypeCount).Aggregate((a, b) => a + b); + + public void Clean() + { + List removals = []; + + foreach (var ns in Namespaces) + { + if (ns.Value.DefinedTypeCount == 0) + { + removals.Add(ns.Key); + } + else + { + ns.Value.Clean(); + } + } + + foreach (var rem in removals) + { + Namespaces.Remove(rem); + } + } } private class NamespaceContext @@ -199,15 +666,33 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt } else if (member is EnumDeclarationSyntax e) { - var en = new TypeContextContainer(namesp, new EnumContext(file, e), e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + var en = new TypeContextContainer(namesp, new EnumContext(file, e), e.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); context.AddTypeContextContainer(ref en, e.Identifier.Text); - Types.Add(e.Identifier.Text, en); + + if (!Types.TryGetValue(e.Identifier.Text, out var list)) + { + list = []; + Types.Add(e.Identifier.Text, list); + } + list.Add(en); } else if (member is TypeDeclarationSyntax t) { - var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, context, usings), t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); - context.AddTypeContextContainer(ref ty, t.Identifier.Text); - Types.Add(t.Identifier.Text, ty); + var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, context, usings), t.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + context.AddTypeContextContainer(ref ty, t.Identifier.Text, t.TypeParameterList is null ? 0 : t.TypeParameterList.Parameters.Count); + + if (!Types.TryGetValue(t.Identifier.Text, out var list)) + { + list = []; + Types.Add(t.Identifier.Text, list); + } + list.Add(ty); } else { @@ -218,17 +703,98 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt Node = node.WithName(IdentifierName(names[0])); } + public NamespaceContext(BaseNamespaceDeclarationSyntax node) + { + Node = node; + } + public BaseNamespaceDeclarationSyntax Node; public Dictionary Namespaces = []; - public Dictionary Types = []; + public Dictionary> Types = []; public BaseNamespaceDeclarationSyntax ToCompletedNode() { List members = []; members.AddRange(Namespaces.Select(n => n.Value.ToCompletedNode())); - members.AddRange(Types.Select(t => t.Value.Type.ToCompletedNode()!)); + foreach (var types in Types) + { + members.AddRange(types.Value.Where(t => t.Type is not null).Select(t => t.Type!.ToCompletedNode()!)); + } return Node.WithMembers(List(members)); } + + public NamespaceContext GetNamespace(string ns) + { + if (ns.Length == 0) + { + return this; + } + + int firstIndex = ns.IndexOf('.'); + if (firstIndex == -1) + { + firstIndex = ns.Length - 1; + } + + string name = ns.Substring(0, firstIndex); + if (!Namespaces.TryGetValue(name, out NamespaceContext? nameSpace)) + { + nameSpace = new NamespaceContext(NamespaceDeclaration(IdentifierName(name))); + Namespaces.Add(name, nameSpace); + } + return nameSpace.GetNamespace(ns.Substring(firstIndex + 1)); + } + + public void Clean() + { + List removals = []; + + foreach (var ns in Namespaces) + { + if (ns.Value.DefinedTypeCount == 0) + { + removals.Add(ns.Key); + } + else + { + ns.Value.Clean(); + } + } + + foreach (var rem in removals) + { + Namespaces.Remove(rem); + } + + removals.Clear(); + foreach (var types in Types) + { + for (int i = 0; i < types.Value.Count; i++) + { + if (types.Value[i].Type is null) + { + types.Value.RemoveAt(i); + i--; + } + else if (types.Value[i].Type is TypeContext ty) + { + ty.Clean(); + } + } + + if (types.Value.Count == 0) + { + removals.Add(types.Key); + } + } + + foreach (var rem in removals) + { + Types.Remove(rem); + } + } + + public int DefinedTypeCount => Types.Select(t => t.Value.Where(ty => ty.Type is not null).Select(ty => ty.Type is TypeContext type ? type.DefinedTypeCount : 1).Aggregate((a, b) => a + b)).Aggregate((a, b) => a + b) + Namespaces.Select(n => n.Value.DefinedTypeCount).Aggregate((a, b) => a + b); } private class TypeContext : IBaseTypeContext @@ -242,16 +808,34 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon if (member is EnumDeclarationSyntax e) { string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{e.Identifier.Text}"; - var en = new TypeContextContainer(ns, new EnumContext(file, e), e.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); + var en = new TypeContextContainer(ns, new EnumContext(file, e), e.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); context.AddTypeContextContainer(ref en, name); - SubTypes.Add(e.Identifier.Text, en); + + if (!SubTypes.TryGetValue(e.Identifier.Text, out var list)) + { + list = []; + SubTypes.Add(e.Identifier.Text, list); + } + list.Add(en); } else if (member is TypeDeclarationSyntax t) { string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{t.Identifier.Text}"; - var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, context, usings, name), t.Modifiers.Any(token => token.IsKind(SyntaxKind.PublicKeyword))); - context.AddTypeContextContainer(ref ty, name); - SubTypes.Add(t.Identifier.Text, ty); + var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, context, usings, name), t.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + context.AddTypeContextContainer(ref ty, name, t.TypeParameterList is null ? 0 : t.TypeParameterList.Parameters.Count); + + if (!SubTypes.TryGetValue(t.Identifier.Text, out var list)) + { + list = []; + SubTypes.Add(t.Identifier.Text, list); + } + list.Add(ty); } else if (member is MethodDeclarationSyntax m) { @@ -280,42 +864,107 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon } } + if (node.BaseList is not null) + { + foreach (var baseType in node.BaseList.Types) + { + BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, ns, usings, this, out int _)); + } + } + Node = node.WithMembers(List(Array.Empty())); } public string File; public TypeDeclarationSyntax Node; - public Dictionary SubTypes = []; + public Dictionary BaseTypes = []; + public Dictionary> SubTypes = []; public Dictionary> Methods = []; public Dictionary Fields = []; public Dictionary Properties = []; public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); + public string FileName => File; + public MemberDeclarationSyntax? ToCompletedNode() { List members = []; - members.AddRange(SubTypes.Select(t => t.Value.Type.ToCompletedNode()!)); + foreach (string type in SubTypes.Keys) + { + members.AddRange(SubTypes[type].Where(t => t.Type is not null).Select(t => t.Type!.ToCompletedNode()!)); + } foreach (string method in Methods.Keys) { members.AddRange(Methods[method].Select(m => m.Node)); } members.AddRange(Fields.Select(f => f.Value.ToCompletedNode())); members.AddRange(Properties.Select(p => p.Value.ToCompletedNode())); - return Node.WithMembers(List(members)); + return Node.WithMembers(List(members)) + .WithBaseList( + BaseList( + SeparatedList(BaseTypes.Select(bType => (BaseTypeSyntax)SimpleBaseType(bType.Value.Type!.Syntax))))); + } + + public int DefinedTypeCount => SubTypes.Select(t => t.Value.Where(ty => ty.Type is not null).Select(ty => ty.Type is TypeContext type ? type.DefinedTypeCount : 1).Aggregate((a, b) => a + b)).Aggregate((a, b) => a + b) + 1; + + public void Clean() + { + List removals = []; + foreach (var types in SubTypes) + { + for (int i = 0; i < types.Value.Count; i++) + { + if (types.Value[i].Type is null) + { + types.Value.RemoveAt(i); + i--; + } + else if (types.Value[i].Type is TypeContext ty) + { + ty.Clean(); + } + } + + if (types.Value.Count == 0) + { + removals.Add(types.Key); + } + } + + foreach(var rem in removals) + { + SubTypes.Remove(rem); + } } } - private class EnumContext : LeafNodeContext, IBaseTypeContext + private class EnumContext : IBaseTypeContext { - public EnumContext(string file, EnumDeclarationSyntax node) : base(node) { File = file; } + public EnumContext(string file, EnumDeclarationSyntax node) + { + File = file; + + foreach (var member in node.Members) + { + Members.Add(member.Identifier.Text, new(member)); + } + + Node = node.WithMembers(SeparatedList(Array.Empty())); + } + + public EnumDeclarationSyntax Node; public string File; public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); + public Dictionary Members = []; + + public string FileName => File; + public MemberDeclarationSyntax? ToCompletedNode() { - return Node; + return Node.WithMembers(SeparatedList(Members.Select(em => em.Value.Node))); } } @@ -323,34 +972,61 @@ private interface IBaseTypeContext { TypeSyntax Syntax { get; } + string FileName { get; } + MemberDeclarationSyntax? ToCompletedNode(); } private class UnknownTypeContext : IBaseTypeContext { - public UnknownTypeContext(TypeSyntax type) { Type = type; } + public UnknownTypeContext(TypeSyntax type) + { + Type = type; + } TypeSyntax Type; public TypeSyntax Syntax => Type; + public string FileName => string.Empty; + public MemberDeclarationSyntax? ToCompletedNode() { return null; } + + + public enum TypeLocation + { + BaseList, + Field, + Property + } } private class TypeContextContainer { - public TypeContextContainer(string ns, IBaseTypeContext ty, bool isPublic) + public TypeContextContainer(string ns, IBaseTypeContext ty, SyntaxKind visibility) { Namespace = ns; Type = ty; - IsPublic = isPublic; + Visibility = visibility; } public string Namespace; - public bool IsPublic; - public IBaseTypeContext Type; + public SyntaxKind Visibility; + public IBaseTypeContext? Type; + + public bool IsPublic => Visibility == SyntaxKind.PublicKeyword; + + public override string ToString() + { + return Type?.Syntax.ToString() ?? string.Empty; + } + } + + private class EnumMemberContext : LeafNodeContext + { + public EnumMemberContext(EnumMemberDeclarationSyntax node) : base(node) { } } private class MethodContext : LeafNodeContext @@ -366,6 +1042,27 @@ public MethodContext(string ns, MethodDeclarationSyntax node, SyntaxContext cont } public Dictionary Parameters = []; + + public override string ToString() + { + return $"{Node.Identifier.Text}({string.Join(',', Parameters.Select(par => $"{par.Value} {par.Key}"))})"; + } + + public static bool operator ==(MethodContext left, MethodContext right) + { + return left.Node.Identifier.Text == right.Node.Identifier.Text && + left.Parameters.Values.SequenceEqual(right.Parameters.Values); + } + + public static bool operator !=(MethodContext left, MethodContext right) + { + return left.Node.Identifier.Text != right.Node.Identifier.Text || + !left.Parameters.Values.SequenceEqual(right.Parameters.Values); + } + + public override bool Equals(object? obj) => base.Equals(obj); + + public override int GetHashCode() => ToString().GetHashCode(); } private class FieldContext : VariableNodes @@ -376,7 +1073,7 @@ private class FieldContext : VariableNodes public FieldDeclarationSyntax ToCompletedNode() { - var type = Container.Type.Syntax; + var type = Container.Type!.Syntax; int pDepth = PointerDepth; while (pDepth > 0) @@ -397,7 +1094,7 @@ private class PropertyContext : VariableNodes public PropertyDeclarationSyntax ToCompletedNode() { - var type = Container.Type.Syntax; + var type = Container.Type!.Syntax; int pDepth = PointerDepth; while (pDepth > 0) From 86470b8263fe52f9e6f6524f77d7b1d073fb9da8 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Sun, 14 Jul 2024 22:50:15 -0400 Subject: [PATCH 04/11] Starting Rewrite Functions -finished Enum rewrite -working on other types rewrite (class, struct, record, etc.) -added new CSharpSyntaxRewriter to hold extra metadata --- .../Clang/ContextCSharpSyntaxRewriter.cs | 34 + sources/SilkTouch/Clang/SyntaxContext.cs | 672 ++++++++++++++++-- .../Mods/Common/ModCSharpSyntaxRewriter.cs | 5 +- 3 files changed, 660 insertions(+), 51 deletions(-) create mode 100644 sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs new file mode 100644 index 0000000000..09cabe69ca --- /dev/null +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// which contains a queryable current context +/// +/// +public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia = false) + : CSharpSyntaxRewriter(visitIntoStructuredTrivia) +{ + /// + /// The current type being visited + /// + public SyntaxContext.IBaseTypeContext? CurrentContext { get; internal set; } + + /// + /// The list of the namespaces being used + /// + public List Usings { get; internal set; } = []; + + /// + /// The namespace of the current context + /// + public string CurrentNamespace { get; internal set; } = string.Empty; +} diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index eaf3ba616c..b6eddbc29e 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection.Metadata; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -13,6 +14,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.VisualBasic.FileIO; using Silk.NET.Core; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -29,6 +31,7 @@ public class SyntaxContext /// public SyntaxContext(GeneratedSyntax syntax) { + Diagnostics = syntax.Diagnostics; //Build initial per file tree foreach ((var fName, var node) in syntax.Files) { @@ -147,16 +150,52 @@ public SyntaxContext(GeneratedSyntax syntax) } } } - - //TODO: Create Full Syntax Tree from individual file trees } Dictionary Files = []; - Dictionary SyntaxTree = []; - Dictionary> TypeDefinitionContainers = []; + IReadOnlyList Diagnostics; + + /// + /// Creates a new based on the current state of this Context + /// + /// + public GeneratedSyntax ToGeneratedSyntax() + { + return new(Files.Select(file => new KeyValuePair(file.Key, file.Value.ToCompletedNode())).ToDictionary(), Diagnostics); + } + + /// + /// Apply Syntax Rewriter on all objects in this Context + /// + /// + public void Rewrite(CSharpSyntaxRewriter rewriter) + { + foreach ((var fName, var context) in Files) + { + + } + } + + private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, out string name, string ns, string file, List usings, string parentName = "") + { + if (type is EnumDeclarationSyntax e) + { + name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{e.Identifier.Text}"; + return new EnumContext(file, e); + + } + else if (type is TypeDeclarationSyntax t) + { + name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{t.Identifier.Text}"; + return new TypeContext(ns, file, t, this, usings, name); + } + name = string.Empty; + return null; + } + private string FindCommonRoot(List paths) { if (paths == null || paths.Count == 0) @@ -664,33 +703,19 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt var nsContext = new NamespaceContext(namesp, ns, context, usings, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } - else if (member is EnumDeclarationSyntax e) + else if (member is BaseTypeDeclarationSyntax bT) { - var en = new TypeContextContainer(namesp, new EnumContext(file, e), e.Modifiers + var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, out string name, namesp, file, usings)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref en, e.Identifier.Text); - if (!Types.TryGetValue(e.Identifier.Text, out var list)) - { - list = []; - Types.Add(e.Identifier.Text, list); - } - list.Add(en); - } - else if (member is TypeDeclarationSyntax t) - { - var ty = new TypeContextContainer(namesp, new TypeContext(namesp, file, t, context, usings), t.Modifiers - .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) - .Select(token => token.Kind()) - .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref ty, t.Identifier.Text, t.TypeParameterList is null ? 0 : t.TypeParameterList.Parameters.Count); + context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); - if (!Types.TryGetValue(t.Identifier.Text, out var list)) + if (!Types.TryGetValue(bT.Identifier.Text, out var list)) { list = []; - Types.Add(t.Identifier.Text, list); + Types.Add(bT.Identifier.Text, list); } list.Add(ty); } @@ -799,41 +824,27 @@ public void Clean() private class TypeContext : IBaseTypeContext { - public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxContext context, List usings, string parentName = "") + public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxContext context, List usings, IBaseTypeContext? parent = null) { + ParentType = parent; File = file; + string parentName = parent is null ? string.Empty : parent.Name; foreach (var member in node.Members) { - if (member is EnumDeclarationSyntax e) + if (member is BaseTypeDeclarationSyntax bT) { - string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{e.Identifier.Text}"; - var en = new TypeContextContainer(ns, new EnumContext(file, e), e.Modifiers + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, parentName)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref en, name); - if (!SubTypes.TryGetValue(e.Identifier.Text, out var list)) - { - list = []; - SubTypes.Add(e.Identifier.Text, list); - } - list.Add(en); - } - else if (member is TypeDeclarationSyntax t) - { - string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{t.Identifier.Text}"; - var ty = new TypeContextContainer(ns, new TypeContext(ns, file, t, context, usings, name), t.Modifiers - .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) - .Select(token => token.Kind()) - .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref ty, name, t.TypeParameterList is null ? 0 : t.TypeParameterList.Parameters.Count); + context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); - if (!SubTypes.TryGetValue(t.Identifier.Text, out var list)) + if (!SubTypes.TryGetValue(bT.Identifier.Text, out var list)) { list = []; - SubTypes.Add(t.Identifier.Text, list); + SubTypes.Add(bT.Identifier.Text, list); } list.Add(ty); } @@ -882,6 +893,11 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public Dictionary> Methods = []; public Dictionary Fields = []; public Dictionary Properties = []; + public IBaseTypeContext? ParentType; + + public string Name => Node.Identifier.Text; + + public IBaseTypeContext? Parent => ParentType; public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); @@ -908,6 +924,10 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public int DefinedTypeCount => SubTypes.Select(t => t.Value.Where(ty => ty.Type is not null).Select(ty => ty.Type is TypeContext type ? type.DefinedTypeCount : 1).Aggregate((a, b) => a + b)).Aggregate((a, b) => a + b) + 1; + public bool IsEnum => false; + + public int GenericParameterCount => Node.TypeParameterList is null ? 0 : Node.TypeParameterList.Parameters.Count; + public void Clean() { List removals = []; @@ -937,12 +957,260 @@ public void Clean() SubTypes.Remove(rem); } } + + public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) + { + member = null; + return false; + } + + public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + { + if (!SubTypes.TryGetValue(typeName, out var value)) + { + childTypeContext = null; + return false; + } + + foreach (var container in value) + { + if (container.Type is not null && container.Type.GenericParameterCount == genericParameterCount) + { + childTypeContext = container.Type; + return true; + } + } + + childTypeContext = null; + return false; + } + + public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + { + if (!Fields.TryGetValue(fieldName, out var context)) + { + pointerDepth = 0; + fieldType = null; + return false; + } + + pointerDepth = context.PointerDepth; + fieldType = context.Container.Type; + return true; + } + + public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + { + if (!Properties.TryGetValue(propertyName, out var context)) + { + pointerDepth = 0; + propertyType = null; + return false; + } + + pointerDepth = context.PointerDepth; + propertyType = context.Container.Type; + return true; + } + + public bool HasBaseType(string baseType) + { + return BaseTypes.ContainsKey(baseType); + } + + public bool HasBaseType(IBaseTypeContext baseType) + { + return BaseTypes.Any(bT => bT.Value.Type == baseType); + } + + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + { + var oldContext = rewriter.CurrentContext; + var oldNamespace = rewriter.CurrentNamespace; + var oldUsings = rewriter.Usings; + + rewriter.CurrentContext = this; + rewriter.CurrentNamespace = ns; + rewriter.Usings = usings; + + var newNode = rewriter.Visit(Node); + + if (newNode is null) + { + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return null; + } + + if (newNode is EnumDeclarationSyntax en) + { + rewriter.CurrentContext = new EnumContext(file, en, this); + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return rewriter.CurrentContext; + } + else if (newNode is TypeDeclarationSyntax ty) + { + Node = ty; + + List removals = []; + Dictionary> newTypes = []; + foreach (var members in SubTypes) + { + for (int i = 0; i < members.Value.Count; i++) + { + var member = members.Value[i]; + if (member.Type is null) + { + continue; + } + var node = member.Type.Rewrite(rewriter, ns, file, context, usings); + + if (node is not TypeContext) + { + members.Value.RemoveAt(i); + i--; + continue; + } + + if (node.Name != member.Type.Name) + { + members.Value.RemoveAt(i); + i--; + + if (SubTypes.TryGetValue(node.Name, out var list2)) + { + list2.Add(member); + } + else + { + newTypes.Add(node.Name, [member]); + } + } + + member.Type = node; + } + + if (members.Value.Count == 0) + { + removals.Add(members.Key); + continue; + } + } + + foreach (var rem in removals) + { + SubTypes.Remove(rem); + } + + foreach (var newMember in newTypes) + { + SubTypes.Add(newMember.Key, newMember.Value); + } + + //TODO: Methods, Fields, and Properties + + foreach (var member in Node.Members) + { + ProcessMember(member, ns, file, context, usings, Parent is null ? string.Empty : Parent.Name); + } + + if (Node.BaseList is not null) + { + foreach (var baseType in Node.BaseList.Types) + { + TryAddBaseType(baseType, context, rewriter); + } + } + + Node = Node.WithMembers(List(Array.Empty())).WithBaseList(BaseList(SeparatedList(Array.Empty()))); + } + else + { + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + throw new Exception("Type Declarations cannot be replaced with non type declarations"); + } + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return rewriter.CurrentContext; + } + + private void ProcessMember(MemberDeclarationSyntax member, string ns, string file, SyntaxContext context, List usings, string parentName) + { + if (member is BaseTypeDeclarationSyntax bT) + { + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, parentName)!, bT.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + + context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); + + if (!SubTypes.TryGetValue(bT.Identifier.Text, out var list)) + { + list = []; + SubTypes.Add(bT.Identifier.Text, list); + } + list.Add(ty); + } + else if (member is MethodDeclarationSyntax m) + { + string name = m.Identifier.Text; + if (!Methods.TryGetValue(name, out var methods)) + { + methods = new(); + Methods.Add(name, methods); + } + methods.Add(new(ns, m, context, usings, parentName, this)); + } + else if (member is FieldDeclarationSyntax f) + { + TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); + + foreach (var dec in f.Declaration.Variables) + { + Fields.Add(dec.Identifier.Text, new(type, pDepth, f.WithDeclaration(f.Declaration.WithVariables(SeparatedList(new[] { dec }))))); + } + } + else if (member is PropertyDeclarationSyntax p) + { + TypeContextContainer type = context.GetTypeContainer(p.Type, ns, usings, this, out int pDepth, parentName); + + Properties.Add(p.Identifier.Text, new(type, pDepth, p)); + } + } + + public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) + { + if (BaseTypes.ContainsKey(baseType.Type.ToString())) + { + BaseTypes[baseType.Type.ToString()] = context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _); + } + else + { + BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _)); + } + return true; + } + + public void RemoveBaseType(BaseTypeSyntax baseType) + { + BaseTypes.Remove(baseType.Type.ToString()); + } } private class EnumContext : IBaseTypeContext { - public EnumContext(string file, EnumDeclarationSyntax node) + public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? parent) { + ParentType = parent; File = file; foreach (var member in node.Members) @@ -956,24 +1224,262 @@ public EnumContext(string file, EnumDeclarationSyntax node) public EnumDeclarationSyntax Node; public string File; + public IBaseTypeContext? ParentType; public TypeSyntax Syntax => IdentifierName(Node.Identifier.Text); public Dictionary Members = []; public string FileName => File; - public MemberDeclarationSyntax? ToCompletedNode() + public string Name => Node.Identifier.Text; + + public IBaseTypeContext? Parent => ParentType; + + public bool IsEnum => true; + + public int GenericParameterCount => 0; + + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + { + var oldContext = rewriter.CurrentContext; + var oldNamespace = rewriter.CurrentNamespace; + var oldUsings = rewriter.Usings; + + rewriter.CurrentContext = this; + rewriter.CurrentNamespace = ns; + rewriter.Usings = usings; + + var newNode = rewriter.Visit(Node); + + if (newNode is null) + { + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return null; + } + + if (newNode is EnumDeclarationSyntax en) + { + Node = en; + + List removals = []; + foreach(var member in Members) + { + var node = rewriter.Visit(member.Value.Node); + + if (node is not EnumMemberDeclarationSyntax em) + { + removals.Add(member.Key); + continue; + } + + member.Value.Node = em; + } + + foreach(var rem in removals) + { + Members.Remove(rem); + } + + Members = Members.ToDictionary(kvp => kvp.Value.Node.Identifier.Text, kvp => kvp.Value); + + + foreach (var member in Node.Members) + { + if (Members.ContainsKey(member.Identifier.Text)) + { + Members[member.Identifier.Text] = new(member); + } + else + { + Members.Add(member.Identifier.Text, new(member)); + } + } + + Node = Node.WithMembers(SeparatedList(Array.Empty())); + } + else if (newNode is TypeDeclarationSyntax ty) + { + var newContext = new TypeContext(ns, file, ty, context, usings, ParentType); + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return newContext; + } + else + { + throw new Exception("Type Declarations cannot be replaced with non type declarations"); + } + + var originalContext = rewriter.CurrentContext; + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return originalContext; + } + + public MemberDeclarationSyntax? ToCompletedNode() => Node.WithMembers(SeparatedList(Members.Select(em => em.Value.Node))); + + public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) + { + if (!Members.TryGetValue(memberName, out var context)) + { + member = null; + return false; + } + + member = context.Node; + return true; + } + + public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + { + childTypeContext = null; + return false; + } + + public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + { + fieldType = null; + pointerDepth = 0; + return false; + } + + public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + { + propertyType = null; + pointerDepth = 0; + return false; + } + public bool HasBaseType(string baseType) + { + return Node.BaseList is not null && Node.BaseList.Types.Any(type => type.Type.ToString() == baseType); + } + public bool HasBaseType(IBaseTypeContext baseType) { - return Node.WithMembers(SeparatedList(Members.Select(em => em.Value.Node))); + return Node.BaseList is not null && Node.BaseList.Types.Any(type => type.Type == baseType.Syntax); } + + public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveBaseType(BaseTypeSyntax baseType) { } } - private interface IBaseTypeContext + /// + /// A queryable representation of a + /// + public interface IBaseTypeContext { + /// + /// A which represents this type + /// TypeSyntax Syntax { get; } + /// + /// The name of the file that contains this type + /// string FileName { get; } + /// + /// The name of the type + /// + string Name { get; } + + /// + /// The containing type context + /// + IBaseTypeContext? Parent { get; } + + /// + /// The number of GenericParameters + /// + int GenericParameterCount { get; } + + /// + /// Does this context represent an enum + /// + bool IsEnum { get; } + + /// + /// Attempts to retrieve information about a enum member + /// + /// + /// + /// + bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member); + + /// + /// Attempts to get Type object that is contained within this type + /// + /// + /// + /// + /// + bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0); + + /// + /// Attempts to get the type and pointer depth of the field in this type + /// + /// + /// + /// how much indirection does this field have + /// + bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth); + + /// + /// Attempts to get the type and pointer depth of the property in this type + /// + /// + /// + /// how much indirection does this field have + /// + bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth); + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(string baseType); + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(IBaseTypeContext baseType); + + /// + /// Attempt to add a new base type for this type to derive from + /// + /// + /// + /// + /// + bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes a type from the list of parent types + /// + /// + void RemoveBaseType(BaseTypeSyntax baseType); + + /// + /// Rewrites the current type with a given rewriter and some metadata + /// + /// + /// + /// + /// + /// + /// + IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings); + + /// + /// Converts this type to a complete + /// + /// MemberDeclarationSyntax? ToCompletedNode(); } @@ -989,11 +1495,75 @@ public UnknownTypeContext(TypeSyntax type) public string FileName => string.Empty; + public string Name => Type.ToString(); + + public IBaseTypeContext? Parent => null; + + public bool IsEnum => false; + + public int GenericParameterCount => Type is GenericNameSyntax generic ? generic.TypeArgumentList.Arguments.Count : 0; + public MemberDeclarationSyntax? ToCompletedNode() { return null; } + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + { + var oldContext = rewriter.CurrentContext; + var oldNamespace = rewriter.CurrentNamespace; + var oldUsings = rewriter.Usings; + + rewriter.CurrentContext = this; + rewriter.CurrentNamespace = ns; + rewriter.Usings = usings; + + var type = rewriter.Visit(Type) as TypeSyntax; + + if (type is not null) + { + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + + return new UnknownTypeContext(type); + } + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + rewriter.Usings = oldUsings; + return null; + } + + public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + { + childTypeContext = null; + return false; + } + + public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + { + fieldType = null; + pointerDepth = 0; + return false; + } + + public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + { + propertyType = null; + pointerDepth = 0; + return false; + } + + public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) + { + member = null; + return false; + } + public bool HasBaseType(string baseType) => false; + public bool HasBaseType(IBaseTypeContext baseType) => false; + public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveBaseType(BaseTypeSyntax baseType) { } public enum TypeLocation { @@ -1108,6 +1678,7 @@ public PropertyDeclarationSyntax ToCompletedNode() } private class VariableNodes : LeafNodeContext + where TNodeType : SyntaxNode { public VariableNodes(TypeContextContainer container, TNodeType node) : base(node) { Container = container; } @@ -1115,8 +1686,11 @@ private class VariableNodes : LeafNodeContext } private class LeafNodeContext(TNodeType node) + where TNodeType: SyntaxNode { public TNodeType Node = node; + + public SyntaxNode? Rewrite(CSharpSyntaxRewriter rewriter) => rewriter.Visit(Node); } /// diff --git a/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs b/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs index ae77feee34..f5a6de60ae 100644 --- a/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Silk.NET.SilkTouch.Clang; using Silk.NET.SilkTouch.Mods.Transformation; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -13,7 +14,7 @@ namespace Silk.NET.SilkTouch.Mods; /// containing common functionality for mods. /// public abstract class ModCSharpSyntaxRewriter(bool visitIntoStructuredTrivia = false) - : CSharpSyntaxRewriter(visitIntoStructuredTrivia), + : ContextCSharpSyntaxRewriter(visitIntoStructuredTrivia), ITransformationContext { private ThreadLocal> _usingsToAdd = From b96c0464cf473f066f242c4d8bdc47411c888a46 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 15 Jul 2024 22:24:35 -0400 Subject: [PATCH 05/11] Working on Rewrite -Added Query functions -Finished Type Rewrites -Stubbed in both Namespace and CompilationUnit Rewrites -Moved IBaseTypeContext out to its own file --- .../Clang/ContextCSharpSyntaxRewriter.cs | 7 +- sources/SilkTouch/Clang/IBaseTypeContext.cs | 288 +++++++++ sources/SilkTouch/Clang/SyntaxContext.cs | 553 ++++++++++++------ 3 files changed, 658 insertions(+), 190 deletions(-) create mode 100644 sources/SilkTouch/Clang/IBaseTypeContext.cs diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs index 09cabe69ca..9897a0b55c 100644 --- a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs @@ -20,7 +20,7 @@ public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia /// /// The current type being visited /// - public SyntaxContext.IBaseTypeContext? CurrentContext { get; internal set; } + public IBaseTypeContext? CurrentContext { get; internal set; } /// /// The list of the namespaces being used @@ -31,4 +31,9 @@ public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia /// The namespace of the current context /// public string CurrentNamespace { get; internal set; } = string.Empty; + + /// + /// The currently SyntaxContext + /// + public SyntaxContext? Context { get; internal set; }; } diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs new file mode 100644 index 0000000000..1c5d2894e2 --- /dev/null +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// A queryable representation of a +/// +public interface IBaseTypeContext +{ + /// + /// A which represents this type + /// + TypeSyntax Syntax { get; } + + /// + /// The name of the file that contains this type + /// + string FileName { get; } + + /// + /// The name of the type + /// + string Name { get; } + + /// + /// The containing type context + /// + IBaseTypeContext? Parent { get; } + + /// + /// The number of GenericParameters + /// + int GenericParameterCount { get; } + + /// + /// The SyntaxNode representing this type + /// + BaseTypeDeclarationSyntax? Node { get; } + + /// + /// Does this context represent an enum + /// + bool IsEnum { get; } + + /// + /// Attempts to retrieve information about a enum member + /// + /// + /// + /// + bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member); + + /// + /// Attempts to Add or overwrites existing enum member within this type + /// + /// + /// + bool TryAddEnumMember(EnumMemberDeclarationSyntax node); + + /// + /// Removes the enum member with the given name within this type + /// + /// + void RemoveEnumMember(string name); + + /// + /// All enumerable members contained within this type + /// + IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers { get; } + + /// + /// Attempts to get Type object that is contained within this type + /// + /// + /// + /// + /// + bool TryGetSubType(string typeName, out SubType type, int genericParameterCount = 0); + + /// + /// Attempts to Add or overwrites a sub type within this type + /// + /// + /// + bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes the subtype with the given name and number of parameters within this type + /// + /// + /// + void RemoveSubType(string name, int genericParameterCount = 0); + + /// + /// Removes all subtypes with the given name within this type + /// + /// + void RemoveSubTypes(string name); + + /// + /// All subtypes contained within this type + /// + IEnumerable<(string, IEnumerable)> SubTypes { get; } + + /// + /// Attempts to get the type and pointer depth of the field in this type + /// + /// + /// + /// + bool TryGetField(string fieldName, out Field field); + + /// + /// Attempts to Add or overwrites a field within this type + /// + /// + /// + bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes the field with the given name within this type + /// + /// + void RemoveField(string name); + + /// + /// All Fields contained within this tyoe + /// + IEnumerable<(string, Field)> Fields { get; } + + /// + /// Attempts to get the type and pointer depth of the property in this type + /// + /// + /// + /// + bool TryGetProperty(string propertyName, out Property property); + + /// + /// Attempts to Add or overwrites a property with the given name within this type + /// + /// + /// + bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes the property with the given name within this type + /// + /// + void RemoveProperty(string name); + + /// + /// All Properties contained within this type + /// + IEnumerable<(string, Property)> Properties { get; } + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(string baseType); + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(BaseTypeSyntax baseType); + + /// + /// Attempts to Add or overwrites a base type for this type to derive from + /// + /// + /// + /// + /// + bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes a type from the list of parent types + /// + /// + void RemoveBaseType(string baseType); + + /// + /// All types this type derives from + /// + IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes { get; } + + /// + /// Attempts to get all methods with a given name and their info + /// + /// + /// + /// + bool TryGetMethods(string name, out IEnumerable methodInfo); + + /// + /// Attempts to Add or overwrites a method within this type + /// + /// + /// + bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes the method with the given name and matching parameters within this type + /// + /// + /// + void RemoveMethod(string name, params TypeSyntax[] parameters); + + /// + /// Removes the methods with the given name within this type + /// + /// + void RemoveMethods(string name); + + /// + /// All methods contained within this type + /// + IEnumerable<(string, IEnumerable)> Methods { get; } + + /// + /// Rewrites the current type with a given rewriter and some metadata + /// + /// + /// + /// + /// + /// + /// + IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings); + + /// + /// Converts this type to a complete + /// + /// + MemberDeclarationSyntax? ToCompletedNode(); + + /// + /// Represents a type defined within a type + /// + /// + /// + public record struct SubType(BaseTypeDeclarationSyntax? Node = null, IBaseTypeContext? TypeContext = null); + + /// + /// Represents a field defined within a type + /// + /// + /// + /// + public record struct Field(FieldDeclarationSyntax? Node = null, IBaseTypeContext? TypeContext = null, int TypePointerDepth = 0); + + /// + /// Represents a property defined within a type + /// + /// + /// + /// + public record struct Property(PropertyDeclarationSyntax? Node = null, IBaseTypeContext? TypeContext = null, int TypePointerDepth = 0); + + /// + /// Represents a method defined within a type + /// + /// + /// + /// + /// + public record struct Method(MethodDeclarationSyntax? Node = null, IBaseTypeContext? ReturnTypeContext = null, int ReturnTypePointerDepth = 0, IEnumerable<(string, MethodParameter)>? Parameters = null); + + /// + /// Represents a parameter of a method defined within a type + /// + /// + /// + /// + public record struct MethodParameter(ParameterSyntax Node, IBaseTypeContext TypeContext, int TypePointerDepth); +} diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index b6eddbc29e..6ef4bf50a8 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -17,6 +17,7 @@ using Microsoft.VisualBasic.FileIO; using Silk.NET.Core; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static Silk.NET.SilkTouch.Clang.SyntaxContext.IBaseTypeContext; namespace Silk.NET.SilkTouch.Clang; @@ -171,26 +172,38 @@ public GeneratedSyntax ToGeneratedSyntax() /// Apply Syntax Rewriter on all objects in this Context /// /// - public void Rewrite(CSharpSyntaxRewriter rewriter) + public void Rewrite(ContextCSharpSyntaxRewriter rewriter) { + rewriter.Context = this; + + List removals = []; + foreach ((var fName, var context) in Files) { - + if (!context.Rewrite(rewriter, fName, this)) + { + removals.Add(fName); + } + } + + foreach (string rem in Removals) + { + Files.Remove(rem); } } - private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, out string name, string ns, string file, List usings, string parentName = "") + private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, out string name, string ns, string file, List usings, IBaseTypeContext? parent = null) { if (type is EnumDeclarationSyntax e) { - name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{e.Identifier.Text}"; - return new EnumContext(file, e); + name = $"{(parent?.Name.Length > 0 ? $"{parent.Name}." : "")}{e.Identifier.Text}"; + return new EnumContext(file, e, parent); } else if (type is TypeDeclarationSyntax t) { - name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{t.Identifier.Text}"; - return new TypeContext(ns, file, t, this, usings, name); + name = $"{(parent?.Name.Length > 0 ? $"{parent.Name}." : "")}{t.Identifier.Text}"; + return new TypeContext(ns, file, t, this, usings, parent); } name = string.Empty; return null; @@ -668,6 +681,29 @@ public void Clean() Namespaces.Remove(rem); } } + + public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxContext context) + { + List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); + + List removals = []; + foreach (var nameSp in Namespaces) + { + if (!nameSp.Value.Rewrite(rewriter, "", file, context, usings)) + { + removals.Add(nameSp.Key); + } + } + + foreach (string rem in removals) + { + Namespaces.Remove(rem); + } + + //TODO: Fix Keys + + return Namespaces.Count > 0; + } } private class NamespaceContext @@ -770,6 +806,78 @@ public NamespaceContext GetNamespace(string ns) return nameSpace.GetNamespace(ns.Substring(firstIndex + 1)); } + public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + { + var node = rewriter.Visit(Node); + + if (node is BaseNamespaceDeclarationSyntax nameSp) + { + Node = nameSp; + //process new members + } + else if (node is TypeDeclarationSyntax ty) + { + //move all members into new TypeContext + } + else + { + return false; + } + + string names = $"{ns}.{Node.Name.ToString()}"; + + List removals = []; + foreach (var name in Namespaces) + { + if (!name.Value.Rewrite(rewriter, names, file, context, usings)) + { + removals.Add(name.Key); + } + } + + foreach (var rem in removals) + { + Namespaces.Remove(rem); + } + + //fix keys + + removals.Clear(); + Dictionary> newTypes = []; + foreach (var types in Types) + { + for (var i = 0; i < types.Value.Count; i++) + { + var type = types.Value[i]; + if (type.Type is null) + { + types.Value.RemoveAt(i); + i--; + continue; + } + type.Type = type.Type.Rewrite(rewriter, names, file, context, usings); + //move if type name has changed + } + + if (types.Value.Count == 0) + { + removals.Add(types.Key); + } + } + + foreach (var rem in removals) + { + Types.Remove(rem); + } + + foreach (var kvp in newTypes) + { + Types.Add(kvp.Key, kvp.Value); + } + + return Namepsaces.Count > 0 || Types.Count > 0; + } + public void Clean() { List removals = []; @@ -834,7 +942,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon { if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, parentName)!, bT.Modifiers + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, this)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -895,6 +1003,22 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public Dictionary Properties = []; public IBaseTypeContext? ParentType; + BaseTypeDeclarationSyntax? IBaseTypeContext.Node => Node; + + public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Array.Empty<(string, EnumMemberDeclarationSyntax)>(); + + IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> IBaseTypeContext.BaseTypes => BaseTypes.Where(bT => bT.Value.Type is not null).Select(bT => (bT.Key, (BaseTypeSyntax)SimpleBaseType(bT.Value.Type!.Syntax), bT.Value.Type!)); + + IEnumerable<(string, IEnumerable)> IBaseTypeContext.SubTypes => SubTypes.Select(types => (types.Key, types.Value.Select(type => new IBaseTypeContext.SubType(type.Type?.Node, type.Type)))); + + IEnumerable<(string, IBaseTypeContext.Field)> IBaseTypeContext.Fields => Fields.Select(field => (field.Key, new IBaseTypeContext.Field(field.Value.Node, field.Value.Container.Type, field.Value.PointerDepth))); + + IEnumerable<(string, IBaseTypeContext.Property)> IBaseTypeContext.Properties => Properties.Select(property => (property.Key, new IBaseTypeContext.Property(property.Value.Node, property.Value.Container.Type, property.Value.PointerDepth))); + + IEnumerable<(string, IEnumerable)> IBaseTypeContext.Methods => Methods.Select(methods => (methods.Key, + methods.Value.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.Type, method.ReturnType.Item2, + method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.Type!, par.Value.PointerDepth))))))); + public string Name => Node.Identifier.Text; public IBaseTypeContext? Parent => ParentType; @@ -958,17 +1082,11 @@ public void Clean() } } - public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) - { - member = null; - return false; - } - - public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) { if (!SubTypes.TryGetValue(typeName, out var value)) { - childTypeContext = null; + subType = new(); return false; } @@ -976,40 +1094,36 @@ public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContex { if (container.Type is not null && container.Type.GenericParameterCount == genericParameterCount) { - childTypeContext = container.Type; + subType = new(container.Type.Node, container.Type); return true; } } - childTypeContext = null; + subType = new(); return false; } - public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) { if (!Fields.TryGetValue(fieldName, out var context)) { - pointerDepth = 0; - fieldType = null; + field = new(); return false; } - pointerDepth = context.PointerDepth; - fieldType = context.Container.Type; + field = new(context.Node, context.Container.Type, context.PointerDepth); return true; } - public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property property) { if (!Properties.TryGetValue(propertyName, out var context)) { - pointerDepth = 0; - propertyType = null; + property = new(); return false; } - pointerDepth = context.PointerDepth; - propertyType = context.Container.Type; + property = new(context.Node, context.Container.Type, context.PointerDepth); return true; } @@ -1018,11 +1132,6 @@ public bool HasBaseType(string baseType) return BaseTypes.ContainsKey(baseType); } - public bool HasBaseType(IBaseTypeContext baseType) - { - return BaseTypes.Any(bT => bT.Value.Type == baseType); - } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) { var oldContext = rewriter.CurrentContext; @@ -1111,11 +1220,9 @@ public bool HasBaseType(IBaseTypeContext baseType) SubTypes.Add(newMember.Key, newMember.Value); } - //TODO: Methods, Fields, and Properties - foreach (var member in Node.Members) { - ProcessMember(member, ns, file, context, usings, Parent is null ? string.Empty : Parent.Name); + ProcessMember(member, ns, file, context, usings); } if (Node.BaseList is not null) @@ -1142,11 +1249,12 @@ public bool HasBaseType(IBaseTypeContext baseType) return rewriter.CurrentContext; } - private void ProcessMember(MemberDeclarationSyntax member, string ns, string file, SyntaxContext context, List usings, string parentName) + private void ProcessMember(MemberDeclarationSyntax member, string ns, string file, SyntaxContext context, List usings) { + string parentName = Parent is null ? string.Empty : Parent.Name; if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, parentName)!, bT.Modifiers + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, this)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -1204,6 +1312,106 @@ public void RemoveBaseType(BaseTypeSyntax baseType) { BaseTypes.Remove(baseType.Type.ToString()); } + + public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) + { + member = null; + return false; + } + public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) => false; + public void RemoveEnumMember(string name) { } + + public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + return true; + } + public void RemoveSubType(string name, int genericParameterCount = 0) + { + if (SubTypes.TryGetValue(name, out var list)) + { + for (int i = 0; i < list.Count; i++) + { + if (list[i].Type?.GenericParameterCount == genericParameterCount) + { + list.RemoveAt(i); + i--; + } + } + + if (list.Count == 0) + { + SubTypes.Remove(name); + } + } + } + + public void RemoveSubTypes(string name) + { + SubTypes.Remove(name); + } + public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + return true; + } + public void RemoveField(string name) + { + Fields.Remove(name); + } + public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + return true; + } + public void RemoveProperty(string name) + { + Properties.Remove(name); + } + public bool HasBaseType(BaseTypeSyntax baseType) => BaseTypes.Any(bT => bT.Value.Type?.Syntax == baseType.Type); + public void RemoveBaseType(string baseType) + { + BaseTypes.Remove(baseType); + } + public bool TryGetMethods(string name, out IEnumerable methodInfo) + { + if (!Methods.TryGetValue(name, out var methods)) + { + methodInfo = Array.Empty(); + return false; + } + methodInfo = methods.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.Type, method.ReturnType.Item2, + method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.Type!, par.Value.PointerDepth))))); + return true; + } + public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + return true; + } + public void RemoveMethod(string name, params TypeSyntax[] parameters) + { + if (Methods.TryGetValue(name, out var list)) + { + for (int i = 0; i < list.Count; i++) + { + if (list[i].Parameters.Select(par => par.Value.Node.Type).SequenceEqual(parameters)) + { + list.RemoveAt(i); + i--; + } + } + + if (list.Count == 0) + { + Methods.Remove(name); + } + } + } + public void RemoveMethods(string name) + { + Methods.Remove(name); + } } private class EnumContext : IBaseTypeContext @@ -1229,6 +1437,21 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa public Dictionary Members = []; + BaseTypeDeclarationSyntax? IBaseTypeContext.Node => Node; + + public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Members.Select(em => (em.Key, em.Value.Node)); + + public IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes => Node.BaseList is null ? Array.Empty<(string, BaseTypeSyntax, IBaseTypeContext)>() : Node.BaseList.Types.Select(type => (type.Type.ToString(), type, (IBaseTypeContext)new UnknownTypeContext(type.Type))); + + public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); + + public IEnumerable<(string, IBaseTypeContext.Field)> Fields => Array.Empty<(string, IBaseTypeContext.Field)>(); + + public IEnumerable<(string, IBaseTypeContext.Property)> Properties => Array.Empty<(string, IBaseTypeContext.Property)>(); + + public IEnumerable<(string, IEnumerable)> Methods => Array.Empty<(string, IEnumerable)>(); + + public string FileName => File; public string Name => Node.Identifier.Text; @@ -1334,153 +1557,65 @@ public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? return true; } - public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + public bool HasBaseType(string baseType) { - childTypeContext = null; - return false; + return Node.BaseList is not null && Node.BaseList.Types.Any(type => type.Type.ToString() == baseType); + } + public bool HasBaseType(BaseTypeSyntax baseType) + { + return Node.BaseList is not null && Node.BaseList.Types.Any(type => type == baseType); } - public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; + public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) { - fieldType = null; - pointerDepth = 0; + subType = new(); return false; } - - public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveSubType(string name, int genericParameterCount = 0) { } + public void RemoveSubTypes(string name) { } + public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) { - propertyType = null; - pointerDepth = 0; + field = new(); return false; } - public bool HasBaseType(string baseType) + + public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveField(string name) { } + public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property property) { - return Node.BaseList is not null && Node.BaseList.Types.Any(type => type.Type.ToString() == baseType); + property = new(); + return false; } - public bool HasBaseType(IBaseTypeContext baseType) + public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveProperty(string name) { } + public void RemoveBaseType(string baseType) { } + public bool TryGetMethods(string name, out IEnumerable methodInfo) { - return Node.BaseList is not null && Node.BaseList.Types.Any(type => type.Type == baseType.Syntax); + methodInfo = Array.Empty(); + return false; } + public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveMethod(string name, params TypeSyntax[] parameters) { } + public void RemoveMethods(string name) { } - public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveBaseType(BaseTypeSyntax baseType) { } - } - - /// - /// A queryable representation of a - /// - public interface IBaseTypeContext - { - /// - /// A which represents this type - /// - TypeSyntax Syntax { get; } - - /// - /// The name of the file that contains this type - /// - string FileName { get; } - - /// - /// The name of the type - /// - string Name { get; } - - /// - /// The containing type context - /// - IBaseTypeContext? Parent { get; } - - /// - /// The number of GenericParameters - /// - int GenericParameterCount { get; } - - /// - /// Does this context represent an enum - /// - bool IsEnum { get; } - - /// - /// Attempts to retrieve information about a enum member - /// - /// - /// - /// - bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member); - - /// - /// Attempts to get Type object that is contained within this type - /// - /// - /// - /// - /// - bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0); - - /// - /// Attempts to get the type and pointer depth of the field in this type - /// - /// - /// - /// how much indirection does this field have - /// - bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth); - - /// - /// Attempts to get the type and pointer depth of the property in this type - /// - /// - /// - /// how much indirection does this field have - /// - bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth); - - /// - /// Does this type inherit from the given type - /// - /// - /// - bool HasBaseType(string baseType); - - /// - /// Does this type inherit from the given type - /// - /// - /// - bool HasBaseType(IBaseTypeContext baseType); - - /// - /// Attempt to add a new base type for this type to derive from - /// - /// - /// - /// - /// - bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter); - - /// - /// Removes a type from the list of parent types - /// - /// - void RemoveBaseType(BaseTypeSyntax baseType); - - /// - /// Rewrites the current type with a given rewriter and some metadata - /// - /// - /// - /// - /// - /// - /// - IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings); - - /// - /// Converts this type to a complete - /// - /// - MemberDeclarationSyntax? ToCompletedNode(); + public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) + { + if (Members.ContainsKey(node.Identifier.Text)) + { + Members[node.Identifier.Text] = new(node); + } + else + { + Members.Add(node.Identifier.Text, new(node)); + } + return true; + } + public void RemoveEnumMember(string name) + { + Members.Remove(name); + } } private class UnknownTypeContext : IBaseTypeContext @@ -1499,10 +1634,24 @@ public UnknownTypeContext(TypeSyntax type) public IBaseTypeContext? Parent => null; + public BaseTypeDeclarationSyntax? Node => null; + public bool IsEnum => false; public int GenericParameterCount => Type is GenericNameSyntax generic ? generic.TypeArgumentList.Arguments.Count : 0; + public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Array.Empty<(string, EnumMemberDeclarationSyntax)>(); + + public IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes => Array.Empty<(string, BaseTypeSyntax, IBaseTypeContext)>(); + + public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); + + public IEnumerable<(string, IBaseTypeContext.Field)> Fields => Array.Empty<(string, IBaseTypeContext.Field)>(); + + public IEnumerable<(string, IBaseTypeContext.Property)> Properties => Array.Empty<(string, IBaseTypeContext.Property)>(); + + public IEnumerable<(string, IEnumerable)> Methods => Array.Empty<(string, IEnumerable)>(); + public MemberDeclarationSyntax? ToCompletedNode() { return null; @@ -1535,35 +1684,48 @@ public UnknownTypeContext(TypeSyntax type) return null; } - public bool TryGetSubType(string typeName, out IBaseTypeContext? childTypeContext, int genericParameterCount = 0) + public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) { - childTypeContext = null; + member = null; return false; } - - public bool TryGetField(string fieldName, out IBaseTypeContext? fieldType, out int pointerDepth) + public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) => false; + public void RemoveEnumMember(string name) { } + public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) { - fieldType = null; - pointerDepth = 0; + subType = new(); return false; } - - public bool TryGetProperty(string propertyName, out IBaseTypeContext? propertyType, out int pointerDepth) + public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveSubType(string name, int genericParameterCount = 0) { } + public void RemoveSubTypes(string name) { } + public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) { - propertyType = null; - pointerDepth = 0; + field = new(); return false; } - public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) + public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveField(string name) { } + public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property property) { - member = null; + property = new(); return false; } + public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveProperty(string name) { } public bool HasBaseType(string baseType) => false; - public bool HasBaseType(IBaseTypeContext baseType) => false; + public bool HasBaseType(BaseTypeSyntax baseType) => false; public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveBaseType(BaseTypeSyntax baseType) { } + public void RemoveBaseType(string baseType) { } + public bool TryGetMethods(string name, out IEnumerable methodInfo) + { + methodInfo = Array.Empty(); + return false; + } + public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveMethod(string name, params TypeSyntax[] parameters) { } + public void RemoveMethods(string name) { } public enum TypeLocation { @@ -1603,15 +1765,17 @@ private class MethodContext : LeafNodeContext { public MethodContext(string ns, MethodDeclarationSyntax node, SyntaxContext context, List usings, string parentName, TypeContext type) : base(node) { + int pDepth; foreach (var para in node.ParameterList.Parameters) { - TypeContextContainer pType = context.GetTypeContainer(para.Type!, ns, usings, type, out int pDepth, parentName); - - Parameters.Add(para.Identifier.Text, pType); + Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parentName, type)); } + + ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, parentName), pDepth); } - public Dictionary Parameters = []; + public Dictionary Parameters = []; + public (TypeContextContainer?, int) ReturnType; public override string ToString() { @@ -1635,6 +1799,17 @@ public override string ToString() public override int GetHashCode() => ToString().GetHashCode(); } + private class MethodParameterContext : LeafNodeContext + { + public MethodParameterContext(ParameterSyntax node, string ns, SyntaxContext context, List usings, string parentName, TypeContext type) : base(node) + { + Type = context.GetTypeContainer(node.Type!, ns, usings, type, out PointerDepth, parentName); + } + + public TypeContextContainer Type; + public int PointerDepth; + } + private class FieldContext : VariableNodes { public FieldContext(TypeContextContainer container, int pointerDepth, FieldDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } From 6d10e0e7469c9a30ab6b373161a57ffd0e2a44fc Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Tue, 16 Jul 2024 21:18:30 -0400 Subject: [PATCH 06/11] Finished Rewrite -also fixed some bugs --- .../Clang/ContextCSharpSyntaxRewriter.cs | 2 +- sources/SilkTouch/Clang/IBaseTypeContext.cs | 6 + sources/SilkTouch/Clang/SyntaxContext.cs | 344 +++++++++++++----- 3 files changed, 269 insertions(+), 83 deletions(-) diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs index 9897a0b55c..a0465ee092 100644 --- a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs @@ -35,5 +35,5 @@ public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia /// /// The currently SyntaxContext /// - public SyntaxContext? Context { get; internal set; }; + public SyntaxContext? Context { get; internal set; } } diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs index 1c5d2894e2..e06e0a45fc 100644 --- a/sources/SilkTouch/Clang/IBaseTypeContext.cs +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -246,6 +246,12 @@ public interface IBaseTypeContext /// MemberDeclarationSyntax? ToCompletedNode(); + /// + /// Changes the parent for this type + /// + /// + void SetParent(IBaseTypeContext? parent); + /// /// Represents a type defined within a type /// diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 6ef4bf50a8..737a42859e 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -8,6 +8,7 @@ using System.Reflection.Metadata; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Linq; using ClangSharp; @@ -17,7 +18,6 @@ using Microsoft.VisualBasic.FileIO; using Silk.NET.Core; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Silk.NET.SilkTouch.Clang.SyntaxContext.IBaseTypeContext; namespace Silk.NET.SilkTouch.Clang; @@ -46,8 +46,97 @@ public SyntaxContext(GeneratedSyntax syntax) } } + MergeCommonTypes(); + } + + Dictionary Files = []; + + Dictionary> TypeDefinitionContainers = []; + + IReadOnlyList Diagnostics; + + /// + /// Creates a new based on the current state of this Context + /// + /// + public GeneratedSyntax ToGeneratedSyntax() + { + return new(Files.Select(file => new KeyValuePair(file.Key, file.Value.ToCompletedNode())).ToDictionary(), Diagnostics); + } + + /// + /// Apply Syntax Rewriter on all objects in this Context + /// + /// + public void Rewrite(ContextCSharpSyntaxRewriter rewriter) + { + rewriter.Context = this; + + List Removals = []; + + foreach ((var fName, var context) in Files) + { + if (!context.Rewrite(rewriter, fName, this)) + { + Removals.Add(fName); + } + } + + foreach (string rem in Removals) + { + Files.Remove(rem); + } + + MergeCommonTypes(); + } + + /// + /// Renames any file matching the old path regex based on the new path regex + /// + /// + /// + public void RenameFile(string oldPathRegex, string newPathRegex) + { + Files = Files.ToDictionary(kvp => Regex.Replace(kvp.Key, oldPathRegex, newPathRegex), kvp => kvp.Value); + } + + /// + /// Removes any file that matches the pathRegex + /// + /// + public void RemoveFile(string pathRegex) + { + List Removals = Files.Keys.Where(key => Regex.IsMatch(key, pathRegex)).ToList(); + + foreach (string rem in Removals) + { + Files.Remove(rem); + } + } + + /// + /// Adds a new file into the SyntaxContext + /// + /// + /// + public void AddFile(string filePath, CompilationUnitSyntax compUnit) + { + if (Files.ContainsKey(filePath)) + { + Files[filePath] = new CompilationContext(filePath, compUnit, this); + } + else + { + Files.Add(filePath, new CompilationContext(filePath, compUnit, this)); + } + + MergeCommonTypes(); + } + + private void MergeCommonTypes() + { //Merge common types into single types - foreach(var typeDef in TypeDefinitionContainers) + foreach (var typeDef in TypeDefinitionContainers) { if (typeDef.Value.Count <= 1) { @@ -138,7 +227,7 @@ public SyntaxContext(GeneratedSyntax syntax) } //finally check all files have types still and either delete them or clean them of bad types - foreach(var file in files) + foreach (var file in files) { if (Files.TryGetValue(file, out var comp) && comp.DefinedTypeCount == 0) { @@ -153,59 +242,17 @@ public SyntaxContext(GeneratedSyntax syntax) } } - Dictionary Files = []; - - Dictionary> TypeDefinitionContainers = []; - - IReadOnlyList Diagnostics; - - /// - /// Creates a new based on the current state of this Context - /// - /// - public GeneratedSyntax ToGeneratedSyntax() - { - return new(Files.Select(file => new KeyValuePair(file.Key, file.Value.ToCompletedNode())).ToDictionary(), Diagnostics); - } - - /// - /// Apply Syntax Rewriter on all objects in this Context - /// - /// - public void Rewrite(ContextCSharpSyntaxRewriter rewriter) - { - rewriter.Context = this; - - List removals = []; - - foreach ((var fName, var context) in Files) - { - if (!context.Rewrite(rewriter, fName, this)) - { - removals.Add(fName); - } - } - - foreach (string rem in Removals) - { - Files.Remove(rem); - } - } - - private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, out string name, string ns, string file, List usings, IBaseTypeContext? parent = null) + private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, string ns, string file, List usings, IBaseTypeContext? parent = null) { if (type is EnumDeclarationSyntax e) { - name = $"{(parent?.Name.Length > 0 ? $"{parent.Name}." : "")}{e.Identifier.Text}"; return new EnumContext(file, e, parent); } else if (type is TypeDeclarationSyntax t) { - name = $"{(parent?.Name.Length > 0 ? $"{parent.Name}." : "")}{t.Identifier.Text}"; return new TypeContext(ns, file, t, this, usings, parent); } - name = string.Empty; return null; } @@ -550,30 +597,30 @@ private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List removals = []; foreach (var nameSp in Namespaces) { - if (!nameSp.Value.Rewrite(rewriter, "", file, context, usings)) + var output = nameSp.Value.Rewrite(rewriter, "", file, context, usings); + if (output.Item1 is null) { removals.Add(nameSp.Key); } + + if (output.Item2 is not null) + { + throw new Exception("Type Declarations not allowed in the global namespace"); + } } foreach (string rem in removals) @@ -700,7 +768,7 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxCon Namespaces.Remove(rem); } - //TODO: Fix Keys + Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); return Namespaces.Count > 0; } @@ -741,12 +809,12 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, out string name, namesp, file, usings)!, bT.Modifiers + var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, namesp, file, usings)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); + context.AddTypeContextContainer(ty); if (!Types.TryGetValue(bT.Identifier.Text, out var list)) { @@ -761,7 +829,7 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt } } - Node = node.WithName(IdentifierName(names[0])); + Node = node.WithName(IdentifierName(names[0])).WithMembers(List(Array.Empty())); } public NamespaceContext(BaseNamespaceDeclarationSyntax node) @@ -806,33 +874,116 @@ public NamespaceContext GetNamespace(string ns) return nameSpace.GetNamespace(ns.Substring(firstIndex + 1)); } - public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + public (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) { var node = rewriter.Visit(Node); + (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) ret; + + string names = $"{ns}.{Node.Name}"; + if (node is BaseNamespaceDeclarationSyntax nameSp) { Node = nameSp; - //process new members + ret = (Node, null); + + names = $"{ns}.{Node.Name}"; + foreach (var member in Node.Members) + { + if (member is BaseNamespaceDeclarationSyntax subns) + { + var nsContext = new NamespaceContext(names, subns, context, usings, file); + Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); + } + else if (member is BaseTypeDeclarationSyntax bT) + { + var ty = new TypeContextContainer(names, context.CreateTypeContextFromNode(bT, names, file, usings)!, bT.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + + context.AddTypeContextContainer(ty); + + if (!Types.TryGetValue(bT.Identifier.Text, out var list)) + { + list = []; + Types.Add(bT.Identifier.Text, list); + } + list.Add(ty); + } + else + { + throw new Exception($"Namespace {Node.Name}{(file != "" ? " in file " + file : "")} contains a member of type ({member.GetType()}) which isn't supported"); + } + } + } - else if (node is TypeDeclarationSyntax ty) + else if (node is TypeDeclarationSyntax type) { - //move all members into new TypeContext + names = ns; + var tyCont = context.CreateTypeContextFromNode(type, ns, file, usings, null) as TypeContext; + var ty = new TypeContextContainer(ns, tyCont!, type.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + + context.AddTypeContextContainer(ty); + + Types = Types.ToDictionary(kvp => $"{tyCont!.Name}." + kvp.Key, kvp => { + foreach (var val in kvp.Value) + { + val.SetParent(tyCont, context); + } + return kvp.Value; + }); + + foreach (var subTypes in Types) + { + if (tyCont!.SubTypes.TryGetValue(subTypes.Key, out var list)) + { + foreach(var sType in subTypes.Value) + { + if (!list.Any(lType => lType.Type?.GenericParameterCount == sType.Type?.GenericParameterCount)) + { + list.Add(sType); + } + } + } + else + { + tyCont.SubTypes.Add(subTypes.Key, subTypes.Value); + } + } + + ret = (null, ty); + + Types = tyCont!.SubTypes; } else { - return false; + return (null, null); } - string names = $"{ns}.{Node.Name.ToString()}"; - List removals = []; foreach (var name in Namespaces) { - if (!name.Value.Rewrite(rewriter, names, file, context, usings)) + var output = name.Value.Rewrite(rewriter, names, file, context, usings); + if (output.Item1 is null) { removals.Add(name.Key); } + + if (output.Item2 is not null) + { + + if (!Types.TryGetValue(output.Item2.Type!.Name, out var list)) + { + list = []; + Types.Add(output.Item2.Type!.Name, list); + } + + list.Add(output.Item2); + } } foreach (var rem in removals) @@ -840,7 +991,7 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file Namespaces.Remove(rem); } - //fix keys + Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); removals.Clear(); Dictionary> newTypes = []; @@ -856,7 +1007,19 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file continue; } type.Type = type.Type.Rewrite(rewriter, names, file, context, usings); - //move if type name has changed + + string name = type.Type!.Name; + if (name != types.Key) + { + if (!Types.TryGetValue(name, out var list) && !newTypes.TryGetValue(name, out list)) + { + list = []; + newTypes.Add(name, list); + } + list.Add(type); + + context.RenameType(type, types.Key); + } } if (types.Value.Count == 0) @@ -875,7 +1038,7 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file Types.Add(kvp.Key, kvp.Value); } - return Namepsaces.Count > 0 || Types.Count > 0; + return Namespaces.Count > 0 || Types.Count > 0 ? ret : (null, null); } public void Clean() @@ -936,18 +1099,20 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon { ParentType = parent; File = file; + Node = node.WithMembers(List(Array.Empty())); + string parentName = parent is null ? string.Empty : parent.Name; foreach (var member in node.Members) { if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, this)!, bT.Modifiers + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, ns, file, usings, this)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); + context.AddTypeContextContainer(ty); if (!SubTypes.TryGetValue(bT.Identifier.Text, out var list)) { @@ -990,8 +1155,6 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, ns, usings, this, out int _)); } } - - Node = node.WithMembers(List(Array.Empty())); } public string File; @@ -1019,7 +1182,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon methods.Value.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.Type, method.ReturnType.Item2, method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.Type!, par.Value.PointerDepth))))))); - public string Name => Node.Identifier.Text; + public string Name => $"{(Parent is null ? string.Empty : $"{Parent.Name}.")}{Node.Identifier.Text}"; public IBaseTypeContext? Parent => ParentType; @@ -1185,7 +1348,8 @@ public bool HasBaseType(string baseType) continue; } - if (node.Name != member.Type.Name) + member.Type = node; + if (members.Key != member.Type.Name) { members.Value.RemoveAt(i); i--; @@ -1198,9 +1362,9 @@ public bool HasBaseType(string baseType) { newTypes.Add(node.Name, [member]); } - } - member.Type = node; + context.RenameType(member, members.Key); + } } if (members.Value.Count == 0) @@ -1254,12 +1418,12 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil string parentName = Parent is null ? string.Empty : Parent.Name; if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, out string name, ns, file, usings, this)!, bT.Modifiers + var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, ns, file, usings, this)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ref ty, name, (bT is not TypeDeclarationSyntax t || t.TypeParameterList is null) ? 0 : t.TypeParameterList.Parameters.Count); + context.AddTypeContextContainer(ty); if (!SubTypes.TryGetValue(bT.Identifier.Text, out var list)) { @@ -1412,6 +1576,8 @@ public void RemoveMethods(string name) { Methods.Remove(name); } + + public void SetParent(IBaseTypeContext? parent) => ParentType = parent; } private class EnumContext : IBaseTypeContext @@ -1420,13 +1586,12 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa { ParentType = parent; File = file; + Node = node.WithMembers(SeparatedList(Array.Empty())); foreach (var member in node.Members) { Members.Add(member.Identifier.Text, new(member)); } - - Node = node.WithMembers(SeparatedList(Array.Empty())); } public EnumDeclarationSyntax Node; @@ -1454,7 +1619,7 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa public string FileName => File; - public string Name => Node.Identifier.Text; + public string Name => $"{(Parent is null ? string.Empty : $"{Parent.Name}.")}{Node.Identifier.Text}"; public IBaseTypeContext? Parent => ParentType; @@ -1616,6 +1781,8 @@ public void RemoveEnumMember(string name) { Members.Remove(name); } + + public void SetParent(IBaseTypeContext? parent) => ParentType = parent; } private class UnknownTypeContext : IBaseTypeContext @@ -1727,6 +1894,8 @@ public bool TryGetMethods(string name, out IEnumerable public void RemoveMethod(string name, params TypeSyntax[] parameters) { } public void RemoveMethods(string name) { } + public void SetParent(IBaseTypeContext? parent) { } + public enum TypeLocation { BaseList, @@ -1748,6 +1917,17 @@ public TypeContextContainer(string ns, IBaseTypeContext ty, SyntaxKind visibilit public SyntaxKind Visibility; public IBaseTypeContext? Type; + public void SetParent(IBaseTypeContext? parent, SyntaxContext context) + { + if (Type is not null) + { + string name = Type.Name; + Type.SetParent(parent); + + context.RenameType(this, name); + } + } + public bool IsPublic => Visibility == SyntaxKind.PublicKeyword; public override string ToString() From 280b5dda1bb48b26c4be0d0d9f0bc91ce5f8aeb7 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 17 Jul 2024 14:50:36 -0400 Subject: [PATCH 07/11] Add missing CompilationUnit Rewrite --- sources/SilkTouch/Clang/SyntaxContext.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 737a42859e..03385708eb 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -746,7 +746,27 @@ public void Clean() public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxContext context) { + var node = rewriter.Visit(Node) as CompilationUnitSyntax; + + if (node is null) + { + return false; + } + List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); + foreach (var member in node.Members) + { + if (member is BaseNamespaceDeclarationSyntax ns) + { + var nsContext = new NamespaceContext(string.Empty, ns, context, usings, file); + Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); + } + else + { + throw new Exception($"CompilationUnit for {file} contains a member of type ({member.GetType()}) which isn't supported"); + } + } + Node = node.WithMembers(List(Array.Empty())); List removals = []; foreach (var nameSp in Namespaces) From a3436e77a497641c2f355748d68c05f3f9df7e11 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 17 Jul 2024 15:34:30 -0400 Subject: [PATCH 08/11] Added Visit functions Also added some missing code for rewrite --- .../Clang/ContextCSharpSyntaxVisitor.cs | 37 ++++ sources/SilkTouch/Clang/IBaseTypeContext.cs | 16 +- sources/SilkTouch/Clang/SyntaxContext.cs | 205 ++++++++++++++---- 3 files changed, 213 insertions(+), 45 deletions(-) create mode 100644 sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs new file mode 100644 index 0000000000..9d9224cbbb --- /dev/null +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// A with metadata for the current Visit method +/// +public class ContextCSharpSyntaxVisitor : CSharpSyntaxVisitor +{ + /// + /// The current type being visited + /// + public IBaseTypeContext? CurrentContext { get; internal set; } + + /// + /// The list of the namespaces being used + /// + public List Usings { get; internal set; } = []; + + /// + /// The namespace of the current context + /// + public string CurrentNamespace { get; internal set; } = string.Empty; + + /// + /// The currently SyntaxContext + /// + public SyntaxContext? Context { get; internal set; } +} diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs index e06e0a45fc..9000be03e4 100644 --- a/sources/SilkTouch/Clang/IBaseTypeContext.cs +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -233,12 +233,18 @@ public interface IBaseTypeContext /// Rewrites the current type with a given rewriter and some metadata /// /// - /// - /// - /// - /// + /// current namespace + /// file this type is in + /// related syntax context + /// available usings /// - IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings); + IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file); + + /// + /// Visits the current type with a given visitor and some metadata + /// + /// + void Visit(ContextCSharpSyntaxVisitor visitor); /// /// Converts this type to a complete diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 03385708eb..91ccadb2e8 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -64,6 +64,20 @@ public GeneratedSyntax ToGeneratedSyntax() return new(Files.Select(file => new KeyValuePair(file.Key, file.Value.ToCompletedNode())).ToDictionary(), Diagnostics); } + /// + /// Visits each node in this context with the provided visitor + /// + /// + public void Visit(ContextCSharpSyntaxVisitor visitor) + { + visitor.Context = this; + + foreach ((var fName, var context) in Files) + { + context.Visit(visitor); + } + } + /// /// Apply Syntax Rewriter on all objects in this Context /// @@ -76,7 +90,7 @@ public void Rewrite(ContextCSharpSyntaxRewriter rewriter) foreach ((var fName, var context) in Files) { - if (!context.Rewrite(rewriter, fName, this)) + if (!context.Rewrite(rewriter, fName)) { Removals.Add(fName); } @@ -744,8 +758,22 @@ public void Clean() } } - public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxContext context) + public void Visit(ContextCSharpSyntaxVisitor visitor) { + List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); + visitor.Usings = usings; + visitor.Visit(Node); + + foreach (var nameSp in Namespaces) + { + nameSp.Value.Visit(visitor, usings); + } + } + + public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file) + { + List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); + rewriter.Usings = usings; var node = rewriter.Visit(Node) as CompilationUnitSyntax; if (node is null) @@ -753,12 +781,12 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxCon return false; } - List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); + usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); foreach (var member in node.Members) { if (member is BaseNamespaceDeclarationSyntax ns) { - var nsContext = new NamespaceContext(string.Empty, ns, context, usings, file); + var nsContext = new NamespaceContext(string.Empty, ns, rewriter.Context!, rewriter.Usings, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else @@ -771,7 +799,7 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file, SyntaxCon List removals = []; foreach (var nameSp in Namespaces) { - var output = nameSp.Value.Rewrite(rewriter, "", file, context, usings); + var output = nameSp.Value.Rewrite(rewriter, "", file); if (output.Item1 is null) { removals.Add(nameSp.Key); @@ -894,35 +922,58 @@ public NamespaceContext GetNamespace(string ns) return nameSpace.GetNamespace(ns.Substring(firstIndex + 1)); } - public (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, string ns = "") + { + ns = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; + visitor.CurrentNamespace = ns; + visitor.Visit(Node); + + + foreach (var name in Namespaces) + { + name.Value.Visit(visitor, usings, ns); + visitor.CurrentNamespace = ns; + } + + visitor.CurrentNamespace = ns; + foreach (var types in Types) + { + foreach (var type in types.Value) + { + type.Type?.Visit(visitor); + } + } + } + + public (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var node = rewriter.Visit(Node); (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) ret; - string names = $"{ns}.{Node.Name}"; + string names = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; if (node is BaseNamespaceDeclarationSyntax nameSp) { Node = nameSp; ret = (Node, null); - names = $"{ns}.{Node.Name}"; + names = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; foreach (var member in Node.Members) { if (member is BaseNamespaceDeclarationSyntax subns) { - var nsContext = new NamespaceContext(names, subns, context, usings, file); + var nsContext = new NamespaceContext(names, subns, rewriter.Context!, rewriter.Usings, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(names, context.CreateTypeContextFromNode(bT, names, file, usings)!, bT.Modifiers + var ty = new TypeContextContainer(names, rewriter.Context!.CreateTypeContextFromNode(bT, names, file, rewriter.Usings)!, bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ty); + rewriter.Context.AddTypeContextContainer(ty); if (!Types.TryGetValue(bT.Identifier.Text, out var list)) { @@ -941,18 +992,18 @@ public NamespaceContext GetNamespace(string ns) else if (node is TypeDeclarationSyntax type) { names = ns; - var tyCont = context.CreateTypeContextFromNode(type, ns, file, usings, null) as TypeContext; + var tyCont = rewriter.Context!.CreateTypeContextFromNode(type, ns, file, rewriter.Usings, null) as TypeContext; var ty = new TypeContextContainer(ns, tyCont!, type.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); - context.AddTypeContextContainer(ty); + rewriter.Context!.AddTypeContextContainer(ty); Types = Types.ToDictionary(kvp => $"{tyCont!.Name}." + kvp.Key, kvp => { foreach (var val in kvp.Value) { - val.SetParent(tyCont, context); + val.SetParent(tyCont, rewriter.Context); } return kvp.Value; }); @@ -987,7 +1038,7 @@ public NamespaceContext GetNamespace(string ns) List removals = []; foreach (var name in Namespaces) { - var output = name.Value.Rewrite(rewriter, names, file, context, usings); + var output = name.Value.Rewrite(rewriter, names, file); if (output.Item1 is null) { removals.Add(name.Key); @@ -1026,7 +1077,7 @@ public NamespaceContext GetNamespace(string ns) i--; continue; } - type.Type = type.Type.Rewrite(rewriter, names, file, context, usings); + type.Type = type.Type.Rewrite(rewriter, names, file); string name = type.Type!.Name; if (name != types.Key) @@ -1038,7 +1089,7 @@ public NamespaceContext GetNamespace(string ns) } list.Add(type); - context.RenameType(type, types.Key); + rewriter.Context!.RenameType(type, types.Key); } } @@ -1315,15 +1366,49 @@ public bool HasBaseType(string baseType) return BaseTypes.ContainsKey(baseType); } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + public void Visit(ContextCSharpSyntaxVisitor visitor) + { + var oldContext = visitor.CurrentContext; + + visitor.CurrentContext = this; + visitor.Visit(Node); + + foreach (var subTypes in SubTypes) + { + foreach (var subType in subTypes.Value) + { + subType.Type?.Visit(visitor); + } + } + + foreach (var field in Fields) + { + visitor.Visit(field.Value.Node); + } + + foreach (var property in Properties) + { + visitor.Visit(property.Value.Node); + } + + foreach (var methods in Methods) + { + foreach (var method in methods.Value) + { + visitor.Visit(method.Node); + } + } + + visitor.CurrentContext = oldContext; + } + + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - var oldUsings = rewriter.Usings; rewriter.CurrentContext = this; rewriter.CurrentNamespace = ns; - rewriter.Usings = usings; var newNode = rewriter.Visit(Node); @@ -1331,7 +1416,6 @@ public bool HasBaseType(string baseType) { rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return null; } @@ -1341,7 +1425,6 @@ public bool HasBaseType(string baseType) rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return rewriter.CurrentContext; } else if (newNode is TypeDeclarationSyntax ty) @@ -1359,7 +1442,7 @@ public bool HasBaseType(string baseType) { continue; } - var node = member.Type.Rewrite(rewriter, ns, file, context, usings); + var node = member.Type.Rewrite(rewriter, ns, file); if (node is not TypeContext) { @@ -1383,7 +1466,7 @@ public bool HasBaseType(string baseType) newTypes.Add(node.Name, [member]); } - context.RenameType(member, members.Key); + rewriter.Context!.RenameType(member, members.Key); } } @@ -1404,16 +1487,59 @@ public bool HasBaseType(string baseType) SubTypes.Add(newMember.Key, newMember.Value); } + List newMembers = new List(); + foreach (var field in Fields) + { + var newMember = rewriter.Visit(field.Value.Node); + + if (newMember is MemberDeclarationSyntax member) + { + newMembers.Add(member); + } + } + + foreach (var property in Properties) + { + var newMember = rewriter.Visit(property.Value.Node); + + if (newMember is MemberDeclarationSyntax member) + { + newMembers.Add(member); + } + } + + foreach (var methods in Methods) + { + foreach (var method in methods.Value) + { + var newMember = rewriter.Visit(method.Node); + + if (newMember is MemberDeclarationSyntax member) + { + newMembers.Add(member); + } + } + } + + Methods.Clear(); + Fields.Clear(); + Properties.Clear(); + + foreach (var member in newMembers) + { + ProcessMember(member, ns, file, rewriter.Context!, rewriter.Usings); + } + foreach (var member in Node.Members) { - ProcessMember(member, ns, file, context, usings); + ProcessMember(member, ns, file, rewriter.Context!, rewriter.Usings); } if (Node.BaseList is not null) { foreach (var baseType in Node.BaseList.Types) { - TryAddBaseType(baseType, context, rewriter); + TryAddBaseType(baseType, rewriter.Context!, rewriter); } } @@ -1423,13 +1549,11 @@ public bool HasBaseType(string baseType) { rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; throw new Exception("Type Declarations cannot be replaced with non type declarations"); } rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return rewriter.CurrentContext; } @@ -1647,15 +1771,23 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa public int GenericParameterCount => 0; - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + public void Visit(ContextCSharpSyntaxVisitor visitor) + { + visitor.Visit(Node); + + foreach (var member in Members) + { + visitor.Visit(member.Value.Node); + } + } + + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - var oldUsings = rewriter.Usings; rewriter.CurrentContext = this; rewriter.CurrentNamespace = ns; - rewriter.Usings = usings; var newNode = rewriter.Visit(Node); @@ -1663,7 +1795,6 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa { rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return null; } @@ -1709,11 +1840,10 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa } else if (newNode is TypeDeclarationSyntax ty) { - var newContext = new TypeContext(ns, file, ty, context, usings, ParentType); + var newContext = new TypeContext(ns, file, ty, rewriter.Context!, rewriter.Usings, ParentType); rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return newContext; } else @@ -1724,7 +1854,6 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa var originalContext = rewriter.CurrentContext; rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return originalContext; } @@ -1844,15 +1973,13 @@ public UnknownTypeContext(TypeSyntax type) return null; } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file, SyntaxContext context, List usings) + public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - var oldUsings = rewriter.Usings; rewriter.CurrentContext = this; rewriter.CurrentNamespace = ns; - rewriter.Usings = usings; var type = rewriter.Visit(Type) as TypeSyntax; @@ -1860,14 +1987,12 @@ public UnknownTypeContext(TypeSyntax type) { rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return new UnknownTypeContext(type); } rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - rewriter.Usings = oldUsings; return null; } From eb9709b1bb06a95f0646674d01e175039e85ea65 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 17 Jul 2024 23:53:57 -0400 Subject: [PATCH 09/11] Adding Missed Types -Adding support for Delegates, Indexers, and Events -Delegates is incomplete --- .../Clang/ContextCSharpSyntaxRewriter.cs | 2 +- sources/SilkTouch/Clang/IBaseTypeContext.cs | 64 +-- sources/SilkTouch/Clang/IDelegateContext.cs | 47 +++ sources/SilkTouch/Clang/IEnumTypeContext.cs | 111 ++++++ sources/SilkTouch/Clang/SyntaxContext.cs | 366 +++++++++++------- sources/SilkTouch/Clang/TypeContainer.cs | 18 + sources/SilkTouch/Mods/Common/IMod.cs | 8 +- sources/SilkTouch/Mods/Common/Mod.cs | 6 +- sources/SilkTouch/SilkTouchGenerator.cs | 13 +- 9 files changed, 438 insertions(+), 197 deletions(-) create mode 100644 sources/SilkTouch/Clang/IDelegateContext.cs create mode 100644 sources/SilkTouch/Clang/IEnumTypeContext.cs create mode 100644 sources/SilkTouch/Clang/TypeContainer.cs diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs index a0465ee092..f16060660b 100644 --- a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs @@ -20,7 +20,7 @@ public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia /// /// The current type being visited /// - public IBaseTypeContext? CurrentContext { get; internal set; } + public TypeContainer? CurrentContext { get; internal set; } /// /// The list of the namespaces being used diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs index 9000be03e4..60925d877f 100644 --- a/sources/SilkTouch/Clang/IBaseTypeContext.cs +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -45,37 +45,6 @@ public interface IBaseTypeContext /// BaseTypeDeclarationSyntax? Node { get; } - /// - /// Does this context represent an enum - /// - bool IsEnum { get; } - - /// - /// Attempts to retrieve information about a enum member - /// - /// - /// - /// - bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member); - - /// - /// Attempts to Add or overwrites existing enum member within this type - /// - /// - /// - bool TryAddEnumMember(EnumMemberDeclarationSyntax node); - - /// - /// Removes the enum member with the given name within this type - /// - /// - void RemoveEnumMember(string name); - - /// - /// All enumerable members contained within this type - /// - IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers { get; } - /// /// Attempts to get Type object that is contained within this type /// @@ -123,7 +92,7 @@ public interface IBaseTypeContext /// /// /// - bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + bool TryAddField(BaseFieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); /// /// Removes the field with the given name within this type @@ -149,7 +118,7 @@ public interface IBaseTypeContext /// /// /// - bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + bool TryAddProperty(BasePropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); /// /// Removes the property with the given name within this type @@ -235,10 +204,8 @@ public interface IBaseTypeContext /// /// current namespace /// file this type is in - /// related syntax context - /// available usings /// - IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file); + TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file); /// /// Visits the current type with a given visitor and some metadata @@ -269,32 +236,41 @@ public record struct SubType(BaseTypeDeclarationSyntax? Node = null, IBaseTypeCo /// Represents a field defined within a type /// /// - /// + /// /// - public record struct Field(FieldDeclarationSyntax? Node = null, IBaseTypeContext? TypeContext = null, int TypePointerDepth = 0); + public record struct Field(BaseFieldDeclarationSyntax? Node = null, TypeContainer? Type = null, int TypePointerDepth = 0); /// /// Represents a property defined within a type /// /// - /// + /// /// - public record struct Property(PropertyDeclarationSyntax? Node = null, IBaseTypeContext? TypeContext = null, int TypePointerDepth = 0); + public record struct Property(BasePropertyDeclarationSyntax? Node = null, TypeContainer? Type = null, int TypePointerDepth = 0); /// /// Represents a method defined within a type /// /// - /// + /// /// /// - public record struct Method(MethodDeclarationSyntax? Node = null, IBaseTypeContext? ReturnTypeContext = null, int ReturnTypePointerDepth = 0, IEnumerable<(string, MethodParameter)>? Parameters = null); + public record struct Method(MethodDeclarationSyntax? Node = null, TypeContainer? ReturnType = null, int ReturnTypePointerDepth = 0, IEnumerable<(string, MethodParameter)>? Parameters = null); /// /// Represents a parameter of a method defined within a type /// /// - /// + /// /// - public record struct MethodParameter(ParameterSyntax Node, IBaseTypeContext TypeContext, int TypePointerDepth); + public record struct MethodParameter(ParameterSyntax Node, TypeContainer? Type, int TypePointerDepth); + + /// + /// Represents a delegate type + /// + /// + /// + /// + /// + public record Delegate(DelegateDeclarationSyntax? Node = null, TypeContainer? ReturnType = null, int ReturnTypePointerDepth = 0, IEnumerable<(string, MethodParameter)>? Parameters = null); } diff --git a/sources/SilkTouch/Clang/IDelegateContext.cs b/sources/SilkTouch/Clang/IDelegateContext.cs new file mode 100644 index 0000000000..b5f969cf5c --- /dev/null +++ b/sources/SilkTouch/Clang/IDelegateContext.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// A representation of a Delegate +/// +public interface IDelegateContext +{ + /// + /// The name of the delegate + /// + string Name { get; } + + /// + /// The syntax node + /// + DelegateDeclarationSyntax? Node { get; } + + /// + /// A representation of each of the parameters + /// + IEnumerable<(string, IBaseTypeContext.MethodParameter)> Parameters { get; } + + /// + /// Rewrites the current type with a given rewriter and some metadata + /// + /// + /// current namespace + /// file this type is in + /// + TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file); + + /// + /// Visits the current type with a given visitor and some metadata + /// + /// + void Visit(ContextCSharpSyntaxVisitor visitor); +} diff --git a/sources/SilkTouch/Clang/IEnumTypeContext.cs b/sources/SilkTouch/Clang/IEnumTypeContext.cs new file mode 100644 index 0000000000..fb2fb19557 --- /dev/null +++ b/sources/SilkTouch/Clang/IEnumTypeContext.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Silk.NET.SilkTouch.Clang; + +public interface IEnumTypeContext +{ + /// + /// A which represents this type + /// + TypeSyntax Syntax { get; } + + /// + /// The name of the file that contains this type + /// + string FileName { get; } + + /// + /// The name of the type + /// + string Name { get; } + + /// + /// The containing type context + /// + IBaseTypeContext? Parent { get; } + + /// + /// The SyntaxNode representing this type + /// + EnumDeclarationSyntax? Node { get; } + + /// + /// Attempts to retrieve information about a enum member + /// + /// + /// + /// + bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member); + + /// + /// Attempts to Add or overwrites existing enum member within this type + /// + /// + /// + bool TryAddEnumMember(EnumMemberDeclarationSyntax node); + + /// + /// Removes the enum member with the given name within this type + /// + /// + void RemoveEnumMember(string name); + + /// + /// All enumerable members contained within this type + /// + IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers { get; } + + /// + /// Rewrites the current type with a given rewriter and some metadata + /// + /// + /// current namespace + /// file this type is in + /// + TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file); + + /// + /// Visits the current type with a given visitor and some metadata + /// + /// + void Visit(ContextCSharpSyntaxVisitor visitor); + + /// + /// Converts this type to a complete + /// + /// + MemberDeclarationSyntax? ToCompletedNode(); + + /// + /// Changes the parent for this type + /// + /// + void SetParent(IBaseTypeContext? parent); + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(string baseType); + + /// + /// Does this type inherit from the given type + /// + /// + /// + bool HasBaseType(BaseTypeSyntax baseType); + + /// + /// All types this type derives from + /// + IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes { get; } +} diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 91ccadb2e8..0e24ac9215 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -5,27 +5,43 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection.Metadata; -using System.Security.Cryptography; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml.Linq; -using ClangSharp; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.VisualBasic.FileIO; -using Silk.NET.Core; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Clang; /// -/// +/// A representation of the full SyntaxTree used to better provide bindings /// public class SyntaxContext { + /// + /// + /// + /// + /// + public SyntaxContext(Dictionary files, IReadOnlyList diagnostics) + { + Diagnostics = diagnostics; + //Build initial per file tree + foreach ((var fName, var node) in files) + { + if (node is CompilationUnitSyntax comp) + { + Files.Add(fName, new CompilationContext(fName, comp, this)); + } + else + { + throw new Exception("CompilationUnitSyntax missing"); + } + } + + MergeCommonTypes(); + } + /// /// /// @@ -49,7 +65,10 @@ public SyntaxContext(GeneratedSyntax syntax) MergeCommonTypes(); } - Dictionary Files = []; + /// + /// Syntax Context data for each file + /// + internal Dictionary Files = []; Dictionary> TypeDefinitionContainers = []; @@ -256,16 +275,16 @@ private void MergeCommonTypes() } } - private IBaseTypeContext? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, string ns, string file, List usings, IBaseTypeContext? parent = null) + private TypeContainer? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, string ns, string file, List usings, IBaseTypeContext? parent = null) { if (type is EnumDeclarationSyntax e) { - return new EnumContext(file, e, parent); + return new(Enum:new EnumContext(file, e, parent)); } else if (type is TypeDeclarationSyntax t) { - return new TypeContext(ns, file, t, this, usings, parent); + return new(new TypeContext(ns, file, t, this, usings, parent)); } return null; } @@ -390,7 +409,7 @@ private void MergeTypeContainers(TypeContextContainer main, List method == mem)) + for (int i = 0; i < list.Count; i++) { - continue; + var mem = list[i]; + if (list.Any(mem => method == mem)) + { + list[i] = mem; + continue; + } } + list.Add(method); } @@ -492,7 +517,7 @@ private void MergeTypeContainers(TypeContextContainer main, List usings, string file = "") { @@ -857,7 +882,7 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, namesp, file, usings)!, bT.Modifiers + var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, namesp, file, usings), bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -968,7 +993,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(names, rewriter.Context!.CreateTypeContextFromNode(bT, names, file, rewriter.Usings)!, bT.Modifiers + var ty = new TypeContextContainer(names, rewriter.Context!.CreateTypeContextFromNode(bT, names, file, rewriter.Usings), bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -992,18 +1017,26 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin else if (node is TypeDeclarationSyntax type) { names = ns; - var tyCont = rewriter.Context!.CreateTypeContextFromNode(type, ns, file, rewriter.Usings, null) as TypeContext; - var ty = new TypeContextContainer(ns, tyCont!, type.Modifiers + var cont = rewriter.Context!.CreateTypeContextFromNode(type, ns, file, rewriter.Usings, null); + TypeContext? tyCont = null; + + if (cont is not null) + { + tyCont = cont.Value.Type as TypeContext; + } + + var ty = new TypeContextContainer(ns, tyCont, type.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); + rewriter.Context!.AddTypeContextContainer(ty); Types = Types.ToDictionary(kvp => $"{tyCont!.Name}." + kvp.Key, kvp => { foreach (var val in kvp.Value) { - val.SetParent(tyCont, rewriter.Context); + val.SetParent(tyCont!, rewriter.Context); } return kvp.Value; }); @@ -1022,7 +1055,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin } else { - tyCont.SubTypes.Add(subTypes.Key, subTypes.Value); + tyCont!.SubTypes.Add(subTypes.Key, subTypes.Value); } } @@ -1077,7 +1110,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin i--; continue; } - type.Type = type.Type.Rewrite(rewriter, names, file); + type.ConvertContainer(type.Type.Rewrite(rewriter, names, file)); string name = type.Type!.Name; if (name != types.Key) @@ -1202,7 +1235,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon } methods.Add(new(ns, m, context, usings, parentName, this)); } - else if (member is FieldDeclarationSyntax f) + else if (member is BaseFieldDeclarationSyntax f) { TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); @@ -1228,6 +1261,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon } } + public string File; public TypeDeclarationSyntax Node; public Dictionary BaseTypes = []; @@ -1239,19 +1273,17 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon BaseTypeDeclarationSyntax? IBaseTypeContext.Node => Node; - public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Array.Empty<(string, EnumMemberDeclarationSyntax)>(); - IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> IBaseTypeContext.BaseTypes => BaseTypes.Where(bT => bT.Value.Type is not null).Select(bT => (bT.Key, (BaseTypeSyntax)SimpleBaseType(bT.Value.Type!.Syntax), bT.Value.Type!)); IEnumerable<(string, IEnumerable)> IBaseTypeContext.SubTypes => SubTypes.Select(types => (types.Key, types.Value.Select(type => new IBaseTypeContext.SubType(type.Type?.Node, type.Type)))); - IEnumerable<(string, IBaseTypeContext.Field)> IBaseTypeContext.Fields => Fields.Select(field => (field.Key, new IBaseTypeContext.Field(field.Value.Node, field.Value.Container.Type, field.Value.PointerDepth))); + IEnumerable<(string, IBaseTypeContext.Field)> IBaseTypeContext.Fields => Fields.Select(field => (field.Key, new IBaseTypeContext.Field(field.Value.Node, field.Value.Container.ToTypeContainer(), field.Value.PointerDepth))); - IEnumerable<(string, IBaseTypeContext.Property)> IBaseTypeContext.Properties => Properties.Select(property => (property.Key, new IBaseTypeContext.Property(property.Value.Node, property.Value.Container.Type, property.Value.PointerDepth))); + IEnumerable<(string, IBaseTypeContext.Property)> IBaseTypeContext.Properties => Properties.Select(property => (property.Key, new IBaseTypeContext.Property(property.Value.Node, property.Value.Container.ToTypeContainer(), property.Value.PointerDepth))); IEnumerable<(string, IEnumerable)> IBaseTypeContext.Methods => Methods.Select(methods => (methods.Key, - methods.Value.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.Type, method.ReturnType.Item2, - method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.Type!, par.Value.PointerDepth))))))); + methods.Value.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.ToTypeContainer(), method.ReturnType.Item2, + method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.ToTypeContainer(), par.Value.PointerDepth))))))); public string Name => $"{(Parent is null ? string.Empty : $"{Parent.Name}.")}{Node.Identifier.Text}"; @@ -1282,8 +1314,6 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public int DefinedTypeCount => SubTypes.Select(t => t.Value.Where(ty => ty.Type is not null).Select(ty => ty.Type is TypeContext type ? type.DefinedTypeCount : 1).Aggregate((a, b) => a + b)).Aggregate((a, b) => a + b) + 1; - public bool IsEnum => false; - public int GenericParameterCount => Node.TypeParameterList is null ? 0 : Node.TypeParameterList.Parameters.Count; public void Clean() @@ -1345,7 +1375,7 @@ public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) return false; } - field = new(context.Node, context.Container.Type, context.PointerDepth); + field = new(context.Node, context.Container.ToTypeContainer(), context.PointerDepth); return true; } @@ -1357,7 +1387,7 @@ public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property pr return false; } - property = new(context.Node, context.Container.Type, context.PointerDepth); + property = new(context.Node, context.Container.ToTypeContainer(), context.PointerDepth); return true; } @@ -1402,12 +1432,12 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) visitor.CurrentContext = oldContext; } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) + public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - rewriter.CurrentContext = this; + rewriter.CurrentContext = new(this); rewriter.CurrentNamespace = ns; var newNode = rewriter.Visit(Node); @@ -1421,11 +1451,12 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) if (newNode is EnumDeclarationSyntax en) { - rewriter.CurrentContext = new EnumContext(file, en, this); + var newContext = new TypeContainer(Enum:new EnumContext(file, en, this)); + rewriter.CurrentContext = newContext; rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - return rewriter.CurrentContext; + return newContext; } else if (newNode is TypeDeclarationSyntax ty) { @@ -1442,28 +1473,27 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) { continue; } - var node = member.Type.Rewrite(rewriter, ns, file); + member.ConvertContainer(member.Type.Rewrite(rewriter, ns, file)); - if (node is not TypeContext) + if (member.IsNull) { members.Value.RemoveAt(i); i--; continue; } - member.Type = node; - if (members.Key != member.Type.Name) + if (members.Key != member.Name) { members.Value.RemoveAt(i); i--; - if (SubTypes.TryGetValue(node.Name, out var list2)) + if (SubTypes.TryGetValue(member.Name, out var list2)) { list2.Add(member); } else { - newTypes.Add(node.Name, [member]); + newTypes.Add(member.Name, [member]); } rewriter.Context!.RenameType(member, members.Key); @@ -1554,7 +1584,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - return rewriter.CurrentContext; + return new(this); } private void ProcessMember(MemberDeclarationSyntax member, string ns, string file, SyntaxContext context, List usings) @@ -1586,7 +1616,7 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil } methods.Add(new(ns, m, context, usings, parentName, this)); } - else if (member is FieldDeclarationSyntax f) + else if (member is BaseFieldDeclarationSyntax f) { TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); @@ -1595,11 +1625,20 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil Fields.Add(dec.Identifier.Text, new(type, pDepth, f.WithDeclaration(f.Declaration.WithVariables(SeparatedList(new[] { dec }))))); } } - else if (member is PropertyDeclarationSyntax p) + else if (member is BasePropertyDeclarationSyntax p) { TypeContextContainer type = context.GetTypeContainer(p.Type, ns, usings, this, out int pDepth, parentName); - Properties.Add(p.Identifier.Text, new(type, pDepth, p)); + string propName = "[]"; + if (member is PropertyDeclarationSyntax prop) + { + propName = prop.Identifier.Text; + } + else if (member is EventDeclarationSyntax even) + { + propName = even.Identifier.Text; + } + Properties.Add(propName, new(type, pDepth, p)); } } @@ -1621,14 +1660,6 @@ public void RemoveBaseType(BaseTypeSyntax baseType) BaseTypes.Remove(baseType.Type.ToString()); } - public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) - { - member = null; - return false; - } - public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) => false; - public void RemoveEnumMember(string name) { } - public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); @@ -1658,7 +1689,7 @@ public void RemoveSubTypes(string name) { SubTypes.Remove(name); } - public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + public bool TryAddField(BaseFieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); return true; @@ -1667,7 +1698,7 @@ public void RemoveField(string name) { Fields.Remove(name); } - public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + public bool TryAddProperty(BasePropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); return true; @@ -1688,8 +1719,8 @@ public bool TryGetMethods(string name, out IEnumerable methodInfo = Array.Empty(); return false; } - methodInfo = methods.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.Type, method.ReturnType.Item2, - method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.Type!, par.Value.PointerDepth))))); + methodInfo = methods.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.ToTypeContainer(), method.ReturnType.Item2, + method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.ToTypeContainer(), par.Value.PointerDepth))))); return true; } public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) @@ -1724,7 +1755,7 @@ public void RemoveMethods(string name) public void SetParent(IBaseTypeContext? parent) => ParentType = parent; } - private class EnumContext : IBaseTypeContext + private class EnumContext : IEnumTypeContext { public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? parent) { @@ -1746,21 +1777,11 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa public Dictionary Members = []; - BaseTypeDeclarationSyntax? IBaseTypeContext.Node => Node; + EnumDeclarationSyntax? IEnumTypeContext.Node => Node; public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Members.Select(em => (em.Key, em.Value.Node)); - public IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes => Node.BaseList is null ? Array.Empty<(string, BaseTypeSyntax, IBaseTypeContext)>() : Node.BaseList.Types.Select(type => (type.Type.ToString(), type, (IBaseTypeContext)new UnknownTypeContext(type.Type))); - public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); - - public IEnumerable<(string, IBaseTypeContext.Field)> Fields => Array.Empty<(string, IBaseTypeContext.Field)>(); - - public IEnumerable<(string, IBaseTypeContext.Property)> Properties => Array.Empty<(string, IBaseTypeContext.Property)>(); - - public IEnumerable<(string, IEnumerable)> Methods => Array.Empty<(string, IEnumerable)>(); - - public string FileName => File; public string Name => $"{(Parent is null ? string.Empty : $"{Parent.Name}.")}{Node.Identifier.Text}"; @@ -1781,12 +1802,12 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) } } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) + public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - rewriter.CurrentContext = this; + rewriter.CurrentContext = new(Enum:this); rewriter.CurrentNamespace = ns; var newNode = rewriter.Visit(Node); @@ -1844,7 +1865,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - return newContext; + return new(newContext); } else { @@ -1880,40 +1901,6 @@ public bool HasBaseType(BaseTypeSyntax baseType) return Node.BaseList is not null && Node.BaseList.Types.Any(type => type == baseType); } - public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) => false; - public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) - { - subType = new(); - return false; - } - public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveSubType(string name, int genericParameterCount = 0) { } - public void RemoveSubTypes(string name) { } - public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) - { - field = new(); - return false; - } - - public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveField(string name) { } - public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property property) - { - property = new(); - return false; - } - public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveProperty(string name) { } - public void RemoveBaseType(string baseType) { } - public bool TryGetMethods(string name, out IEnumerable methodInfo) - { - methodInfo = Array.Empty(); - return false; - } - public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; - public void RemoveMethod(string name, params TypeSyntax[] parameters) { } - public void RemoveMethods(string name) { } - public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) { if (Members.ContainsKey(node.Identifier.Text)) @@ -1952,12 +1939,8 @@ public UnknownTypeContext(TypeSyntax type) public BaseTypeDeclarationSyntax? Node => null; - public bool IsEnum => false; - public int GenericParameterCount => Type is GenericNameSyntax generic ? generic.TypeArgumentList.Arguments.Count : 0; - public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Array.Empty<(string, EnumMemberDeclarationSyntax)>(); - public IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes => Array.Empty<(string, BaseTypeSyntax, IBaseTypeContext)>(); public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); @@ -1973,12 +1956,17 @@ public UnknownTypeContext(TypeSyntax type) return null; } - public IBaseTypeContext? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) + public void Visit(ContextCSharpSyntaxVisitor visitor) + { + visitor.Visit(Node); + } + + public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; var oldNamespace = rewriter.CurrentNamespace; - rewriter.CurrentContext = this; + rewriter.CurrentContext = new(this); rewriter.CurrentNamespace = ns; var type = rewriter.Visit(Type) as TypeSyntax; @@ -1988,7 +1976,7 @@ public UnknownTypeContext(TypeSyntax type) rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; - return new UnknownTypeContext(type); + return new(new UnknownTypeContext(type)); } rewriter.CurrentContext = oldContext; @@ -1996,13 +1984,6 @@ public UnknownTypeContext(TypeSyntax type) return null; } - public bool TryGetEnumMember(string memberName, out EnumMemberDeclarationSyntax? member) - { - member = null; - return false; - } - public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) => false; - public void RemoveEnumMember(string name) { } public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) { subType = new(); @@ -2017,14 +1998,14 @@ public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) return false; } - public bool TryAddField(FieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public bool TryAddField(BaseFieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; public void RemoveField(string name) { } public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property property) { property = new(); return false; } - public bool TryAddProperty(PropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public bool TryAddProperty(BasePropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; public void RemoveProperty(string name) { } public bool HasBaseType(string baseType) => false; public bool HasBaseType(BaseTypeSyntax baseType) => false; @@ -2049,18 +2030,113 @@ public enum TypeLocation } } - private class TypeContextContainer + private class DelegateContext : LeafNodeContext, IDelegateContext { - public TypeContextContainer(string ns, IBaseTypeContext ty, SyntaxKind visibility) + public DelegateContext(DelegateDeclarationSyntax node, string ns, SyntaxContext context, List usings, IBaseTypeContext? parent, TypeContext? type) : base(node) + { + int pDepth; + foreach (var para in node.ParameterList.Parameters) + { + Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, type)); + } + + ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, parent?.Name ?? string.Empty), pDepth); + } + + public Dictionary Parameters = []; + public (TypeContextContainer?, int) ReturnType; + public IBaseTypeContext? Parent; + + public string Name => $"{(Parent is null ? "" : $"{Parent.Name}.")}{Node.Identifier.Text}"; + + DelegateDeclarationSyntax? IDelegateContext.Node => throw new NotImplementedException(); + + IEnumerable<(string, IBaseTypeContext.MethodParameter)> IDelegateContext.Parameters => Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.ToTypeContainer(), par.Value.PointerDepth))); + + public static bool operator ==(DelegateContext left, DelegateContext right) + { + return left.Name == right.Name && + left.Parameters.Values.SequenceEqual(right.Parameters.Values); + } + + public static bool operator !=(DelegateContext left, DelegateContext right) + { + return left.Name != right.Name || + !left.Parameters.Values.SequenceEqual(right.Parameters.Values); + } + + public override bool Equals(object? obj) => base.Equals(obj); + + public override int GetHashCode() => ToString().GetHashCode(); + + public override string ToString() + { + return $"{Node.Identifier.Text}({string.Join(',', Parameters.Select(par => $"{par.Value} {par.Key}"))})"; + } + + public static DelegateContext? ToDelegateContext(IDelegateContext context) + { + return context as DelegateContext; + } + + public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) => throw new NotImplementedException(); + public void Visit(ContextCSharpSyntaxVisitor visitor) => throw new NotImplementedException(); + } + + internal class TypeContextContainer + { + public TypeContextContainer(string ns, IBaseTypeContext? ty, IEnumTypeContext? en, IDelegateContext? del, SyntaxKind visibility) { Namespace = ns; Type = ty; Visibility = visibility; + Delegate = del; + Enum = en; + } + + public TypeContextContainer(string ns, TypeContainer? container, SyntaxKind visibility) + { + Namespace = ns; + Type = container?.Type; + Delegate = container?.Delegate; + Enum = container?.Enum; + Visibility = visibility; + } + + public TypeContextContainer(string ns, IBaseTypeContext? ty, SyntaxKind visibility) + { + Namespace = ns; + Type = ty; + Visibility = visibility; + Delegate = null; + Enum = null; + } + + public TypeContextContainer(string ns, IEnumTypeContext? en, SyntaxKind visibility) + { + Namespace = ns; + Type = null; + Visibility = visibility; + Delegate = null; + Enum = en; + } + + public TypeContextContainer(string ns, IDelegateContext? del, SyntaxKind visibility) + { + Namespace = ns; + Type = null; + Visibility = visibility; + Delegate = del; + Enum = null; } public string Namespace; public SyntaxKind Visibility; public IBaseTypeContext? Type; + public IEnumTypeContext? Enum; + public IDelegateContext? Delegate; + + public string Name => Type is not null ? Type.Name : (Enum is not null ? Enum.Name : (Delegate is not null ? Delegate.Name : string.Empty)); public void SetParent(IBaseTypeContext? parent, SyntaxContext context) { @@ -2075,10 +2151,24 @@ public void SetParent(IBaseTypeContext? parent, SyntaxContext context) public bool IsPublic => Visibility == SyntaxKind.PublicKeyword; + public bool IsNull => Type is null && Enum is null && Delegate is null; + public override string ToString() { return Type?.Syntax.ToString() ?? string.Empty; } + + public TypeContainer ToTypeContainer() + { + return new(Type, Enum, Delegate); + } + + public void ConvertContainer(TypeContainer? container) + { + Type = container?.Type; + Enum = container?.Enum; + Delegate = container?.Delegate; + } } private class EnumMemberContext : LeafNodeContext @@ -2126,7 +2216,7 @@ public override string ToString() private class MethodParameterContext : LeafNodeContext { - public MethodParameterContext(ParameterSyntax node, string ns, SyntaxContext context, List usings, string parentName, TypeContext type) : base(node) + public MethodParameterContext(ParameterSyntax node, string ns, SyntaxContext context, List usings, string parentName, TypeContext? type) : base(node) { Type = context.GetTypeContainer(node.Type!, ns, usings, type, out PointerDepth, parentName); } @@ -2135,13 +2225,13 @@ public MethodParameterContext(ParameterSyntax node, string ns, SyntaxContext con public int PointerDepth; } - private class FieldContext : VariableNodes + private class FieldContext : VariableNodes { - public FieldContext(TypeContextContainer container, int pointerDepth, FieldDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + public FieldContext(TypeContextContainer container, int pointerDepth, BaseFieldDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } public int PointerDepth; - public FieldDeclarationSyntax ToCompletedNode() + public BaseFieldDeclarationSyntax ToCompletedNode() { var type = Container.Type!.Syntax; @@ -2156,13 +2246,13 @@ public FieldDeclarationSyntax ToCompletedNode() } } - private class PropertyContext : VariableNodes + private class PropertyContext : VariableNodes { - public PropertyContext(TypeContextContainer container, int pointerDepth, PropertyDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + public PropertyContext(TypeContextContainer container, int pointerDepth, BasePropertyDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } public int PointerDepth; - public PropertyDeclarationSyntax ToCompletedNode() + public BasePropertyDeclarationSyntax ToCompletedNode() { var type = Container.Type!.Syntax; diff --git a/sources/SilkTouch/Clang/TypeContainer.cs b/sources/SilkTouch/Clang/TypeContainer.cs new file mode 100644 index 0000000000..e2c5ce1c47 --- /dev/null +++ b/sources/SilkTouch/Clang/TypeContainer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// A container to represent a potential Type/Delegate since often the two can be used interchangably +/// +/// +/// +/// +public record struct TypeContainer(IBaseTypeContext? Type = null, IEnumTypeContext? Enum = null, IDelegateContext? Delegate = null); diff --git a/sources/SilkTouch/Mods/Common/IMod.cs b/sources/SilkTouch/Mods/Common/IMod.cs index 22a4d77c3e..2dc695a073 100644 --- a/sources/SilkTouch/Mods/Common/IMod.cs +++ b/sources/SilkTouch/Mods/Common/IMod.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Silk.NET.SilkTouch.Clang; @@ -40,13 +40,13 @@ Task> BeforeScrapeAsync(string key, List rsps) /// opportunity to mutate the syntax tree. /// /// The job name (corresponds to the configuration key for mod configs). - /// The generated output from ClangSharp (or the previous mod). + /// The generated output from ClangSharp (or the previous mod). /// /// The modified syntax nodes to be either passed to the next mod or output from the generator if this is the last /// mod. /// - Task AfterScrapeAsync(string key, GeneratedSyntax syntax) => - Task.FromResult(syntax); + Task AfterScrapeAsync(string key, SyntaxContext context) => + Task.FromResult(context); /// /// Runs before SilkTouch is going to output the MSBuild workspace. The generated documents have already been added, diff --git a/sources/SilkTouch/Mods/Common/Mod.cs b/sources/SilkTouch/Mods/Common/Mod.cs index d8d2c84443..518d6cdc24 100644 --- a/sources/SilkTouch/Mods/Common/Mod.cs +++ b/sources/SilkTouch/Mods/Common/Mod.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Silk.NET.SilkTouch.Clang; @@ -55,8 +55,8 @@ public virtual Task> BeforeScrapeAsync(string key, List - public virtual Task AfterScrapeAsync(string key, GeneratedSyntax syntax) => - Task.FromResult(syntax); + public virtual Task AfterScrapeAsync(string key, SyntaxContext context) => + Task.FromResult(context); /// public virtual Task BeforeOutputAsync( diff --git a/sources/SilkTouch/SilkTouchGenerator.cs b/sources/SilkTouch/SilkTouchGenerator.cs index 2a91254e37..a0aa4196c6 100644 --- a/sources/SilkTouch/SilkTouchGenerator.cs +++ b/sources/SilkTouch/SilkTouchGenerator.cs @@ -254,9 +254,7 @@ var file in await cacheProvider.GetFiles( kvp => CSharpSyntaxTree.ParseText(SourceText.From(kvp.Value)).GetRoot() ); rawBindings.Files.Clear(); // GC ASAP - var bindings = new GeneratedSyntax(syntaxTrees, rawBindings.Diagnostics); - - var context = new SyntaxContext( bindings ); + var context = new SyntaxContext(syntaxTrees, rawBindings.Diagnostics); // Mod the bindings // ReSharper disable once LoopCanBeConvertedToQuery @@ -267,7 +265,7 @@ var file in await cacheProvider.GetFiles( mod.GetType().Name, key ); - bindings = await mod.AfterScrapeAsync(key, bindings); + context = await mod.AfterScrapeAsync(key, context); } // Add a license header to files that don't have one @@ -279,8 +277,9 @@ await File.ReadAllLinesAsync(job.DefaultLicenseHeader, ct) .Where(x => x.Length == 0 || x.StartsWith("//")) .Select(x => Comment(x.Trim())) .ToArray(); - foreach (var (file, node) in bindings.Files) + foreach (var (file, comp) in context.Files) { + var node = comp.Node; var shouldAddHeader = !node.GetLeadingTrivia() .Any(x => x.Kind() is SyntaxKind.SingleLineCommentTrivia) @@ -292,7 +291,7 @@ await File.ReadAllLinesAsync(job.DefaultLicenseHeader, ct) ).GetValueOrDefault(); if (shouldAddHeader) { - bindings.Files[file] = node.WithLeadingTrivia( + comp.Node = node.WithLeadingTrivia( defaultLicenseHeaderTrivia.Concat(node.GetLeadingTrivia()) ); } @@ -304,7 +303,7 @@ await File.ReadAllLinesAsync(job.DefaultLicenseHeader, ct) "Bindings generation completed in {} seconds, writing to disk...", sw.Elapsed.TotalSeconds ); - return bindings; + return context.ToGeneratedSyntax(); } /// From 89293c45b10a0bbde2f80757066b73ceea7083a3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Thu, 18 Jul 2024 14:58:41 -0400 Subject: [PATCH 10/11] Finish up Delegate work -added some type cleanup as well --- sources/SilkTouch/Clang/IBaseTypeContext.cs | 5 + sources/SilkTouch/Clang/IDelegateContext.cs | 5 + sources/SilkTouch/Clang/SyntaxContext.cs | 233 ++++++++++++++++---- 3 files changed, 200 insertions(+), 43 deletions(-) diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs index 60925d877f..7638ffb090 100644 --- a/sources/SilkTouch/Clang/IBaseTypeContext.cs +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -225,6 +225,11 @@ public interface IBaseTypeContext /// void SetParent(IBaseTypeContext? parent); + /// + /// Sets all subTypes to null to be cleaned later + /// + void Delete(); + /// /// Represents a type defined within a type /// diff --git a/sources/SilkTouch/Clang/IDelegateContext.cs b/sources/SilkTouch/Clang/IDelegateContext.cs index b5f969cf5c..dc04ad1200 100644 --- a/sources/SilkTouch/Clang/IDelegateContext.cs +++ b/sources/SilkTouch/Clang/IDelegateContext.cs @@ -25,6 +25,11 @@ public interface IDelegateContext /// DelegateDeclarationSyntax? Node { get; } + /// + /// The containing parent type if this delegate is contained within a type + /// + IBaseTypeContext? Parent { get; } + /// /// A representation of each of the parameters /// diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 0e24ac9215..63ddd671d5 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -6,6 +6,8 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Xml.Linq; +using ClangSharp; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -178,6 +180,12 @@ private void MergeCommonTypes() for (int i = 0; i < typeDef.Value.Count; i++) { + if (typeDef.Value[i].IsNull) + { + typeDef.Value.RemoveAt(i); + i--; + continue; + } List typesToMerge = []; TypeContextContainer main = typeDef.Value[i]; string mainFile = typeDef.Value.Where(t => t.Type?.FileName.Length > 0).First().Type!.FileName; @@ -268,7 +276,7 @@ private void MergeCommonTypes() } else if (comp is not null) { - comp.Clean(); + comp.Clean(this); } } } @@ -761,8 +769,9 @@ public NamespaceContext GetNamespace(string ns) public int DefinedTypeCount => Namespaces.Select(n => n.Value.DefinedTypeCount).Aggregate((a, b) => a + b); - public void Clean() + public void Clean(SyntaxContext context) { + List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); List removals = []; foreach (var ns in Namespaces) @@ -773,7 +782,7 @@ public void Clean() } else { - ns.Value.Clean(); + ns.Value.Clean("", context, usings); } } @@ -1018,14 +1027,8 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin { names = ns; var cont = rewriter.Context!.CreateTypeContextFromNode(type, ns, file, rewriter.Usings, null); - TypeContext? tyCont = null; - if (cont is not null) - { - tyCont = cont.Value.Type as TypeContext; - } - - var ty = new TypeContextContainer(ns, tyCont, type.Modifiers + var ty = new TypeContextContainer(ns, cont, type.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -1033,35 +1036,49 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin rewriter.Context!.AddTypeContextContainer(ty); - Types = Types.ToDictionary(kvp => $"{tyCont!.Name}." + kvp.Key, kvp => { + Types = Types.ToDictionary(kvp => $"{ty.Name}." + kvp.Key, kvp => { foreach (var val in kvp.Value) { - val.SetParent(tyCont!, rewriter.Context); + val.SetParent(cont!.Value.Type, rewriter.Context); } return kvp.Value; }); - foreach (var subTypes in Types) + ret = (null, ty); + + if (cont?.Type is TypeContext tyCont) { - if (tyCont!.SubTypes.TryGetValue(subTypes.Key, out var list)) + foreach (var subTypes in Types) { - foreach(var sType in subTypes.Value) + if (tyCont!.SubTypes.TryGetValue(subTypes.Key, out var list)) { - if (!list.Any(lType => lType.Type?.GenericParameterCount == sType.Type?.GenericParameterCount)) + foreach (var sType in subTypes.Value) { - list.Add(sType); + if (!list.Any(lType => lType.Type?.GenericParameterCount == sType.Type?.GenericParameterCount)) + { + list.Add(sType); + } } } + else + { + tyCont!.SubTypes.Add(subTypes.Key, subTypes.Value); + } } - else + Types = tyCont!.SubTypes; + } + else + { + foreach (var subTypes in Types) { - tyCont!.SubTypes.Add(subTypes.Key, subTypes.Value); + foreach (var sType in subTypes.Value) + { + sType.Delete(); + } } - } - ret = (null, ty); - - Types = tyCont!.SubTypes; + return ret; + } } else { @@ -1145,19 +1162,20 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin return Namespaces.Count > 0 || Types.Count > 0 ? ret : (null, null); } - public void Clean() + public void Clean(string ns, SyntaxContext context, List usings) { + ns = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; List removals = []; - foreach (var ns in Namespaces) + foreach (var nameSp in Namespaces) { - if (ns.Value.DefinedTypeCount == 0) + if (nameSp.Value.DefinedTypeCount == 0) { - removals.Add(ns.Key); + removals.Add(nameSp.Key); } else { - ns.Value.Clean(); + nameSp.Value.Clean(ns, context, usings); } } @@ -1171,14 +1189,14 @@ public void Clean() { for (int i = 0; i < types.Value.Count; i++) { - if (types.Value[i].Type is null) + if (types.Value[i].IsNull) { types.Value.RemoveAt(i); i--; } else if (types.Value[i].Type is TypeContext ty) { - ty.Clean(); + ty.Clean(ns, context, usings); } } @@ -1316,21 +1334,25 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public int GenericParameterCount => Node.TypeParameterList is null ? 0 : Node.TypeParameterList.Parameters.Count; - public void Clean() + public void Clean(string ns, SyntaxContext context, List usings) { List removals = []; foreach (var types in SubTypes) { for (int i = 0; i < types.Value.Count; i++) { - if (types.Value[i].Type is null) + if (types.Value[i].IsNull) { types.Value.RemoveAt(i); i--; } else if (types.Value[i].Type is TypeContext ty) { - ty.Clean(); + ty.Clean(ns, context, usings); + } + else if (types.Value[i].Delegate is DelegateContext del) + { + del.RefreshTypeLinks(ns, context, usings, this); } } @@ -1340,7 +1362,25 @@ public void Clean() } } - foreach(var rem in removals) + foreach (var field in Fields) + { + field.Value.RefreshTypeLinks(ns, context, usings, this); + } + + foreach (var prop in Properties) + { + prop.Value.RefreshTypeLinks(ns, context, usings, this); + } + + foreach (var methods in Methods) + { + foreach (var method in methods.Value) + { + method.RefreshTypeLinks(ns, context, usings, this); + } + } + + foreach (var rem in removals) { SubTypes.Remove(rem); } @@ -1451,8 +1491,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) if (newNode is EnumDeclarationSyntax en) { - var newContext = new TypeContainer(Enum:new EnumContext(file, en, this)); - rewriter.CurrentContext = newContext; + var newContext = new TypeContainer(Enum:new EnumContext(file, en, Parent)); rewriter.CurrentContext = oldContext; rewriter.CurrentNamespace = oldNamespace; @@ -1575,6 +1614,14 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) Node = Node.WithMembers(List(Array.Empty())).WithBaseList(BaseList(SeparatedList(Array.Empty()))); } + else if (newNode is DelegateDeclarationSyntax del) + { + var newContext = new TypeContainer(Delegate: new DelegateContext(del, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, Parent)); + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + return newContext; + } else { rewriter.CurrentContext = oldContext; @@ -1753,6 +1800,16 @@ public void RemoveMethods(string name) } public void SetParent(IBaseTypeContext? parent) => ParentType = parent; + public void Delete() + { + foreach (var subTypes in SubTypes) + { + foreach (var subType in subTypes.Value) + { + subType.Delete(); + } + } + } } private class EnumContext : IEnumTypeContext @@ -1867,6 +1924,14 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) rewriter.CurrentNamespace = oldNamespace; return new(newContext); } + else if (newNode is DelegateDeclarationSyntax del) + { + var newContext = new TypeContainer(Delegate: new DelegateContext(del, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, ParentType)); + + rewriter.CurrentContext = oldContext; + rewriter.CurrentNamespace = oldNamespace; + return newContext; + } else { throw new Exception("Type Declarations cannot be replaced with non type declarations"); @@ -2022,6 +2087,8 @@ public void RemoveMethods(string name) { } public void SetParent(IBaseTypeContext? parent) { } + public void Delete() { } + public enum TypeLocation { BaseList, @@ -2032,22 +2099,38 @@ public enum TypeLocation private class DelegateContext : LeafNodeContext, IDelegateContext { - public DelegateContext(DelegateDeclarationSyntax node, string ns, SyntaxContext context, List usings, IBaseTypeContext? parent, TypeContext? type) : base(node) + public DelegateContext(DelegateDeclarationSyntax node, string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) : base(node) { + ParentType = parent; + int pDepth; foreach (var para in node.ParameterList.Parameters) { - Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, type)); + Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ParentType as TypeContext)); + } + + ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.Name ?? string.Empty), pDepth); + } + + public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) + { + int pDepth; + Parameters.Clear(); + foreach (var para in Node.ParameterList.Parameters) + { + Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ParentType as TypeContext)); } - ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, parent?.Name ?? string.Empty), pDepth); + ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.Name ?? string.Empty), pDepth); } public Dictionary Parameters = []; public (TypeContextContainer?, int) ReturnType; - public IBaseTypeContext? Parent; + public IBaseTypeContext? ParentType; + + public IBaseTypeContext? Parent => ParentType; - public string Name => $"{(Parent is null ? "" : $"{Parent.Name}.")}{Node.Identifier.Text}"; + public string Name => $"{(ParentType is null ? "" : $"{ParentType.Name}.")}{Node.Identifier.Text}"; DelegateDeclarationSyntax? IDelegateContext.Node => throw new NotImplementedException(); @@ -2079,8 +2162,38 @@ public override string ToString() return context as DelegateContext; } - public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) => throw new NotImplementedException(); - public void Visit(ContextCSharpSyntaxVisitor visitor) => throw new NotImplementedException(); + public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) + { + var node = rewriter.Visit(Node); + + if (node is DelegateDeclarationSyntax del) + { + Node = del; + RefreshTypeLinks(ns, rewriter.Context!, rewriter.Usings, Parent); + } + else if (node is EnumDeclarationSyntax en) + { + var newContext = new TypeContainer(Enum: new EnumContext(file, en, Parent)); + + return newContext; + } + else if (node is TypeDeclarationSyntax ty) + { + var newContext = new TypeContext(ns, file, ty, rewriter.Context!, rewriter.Usings, ParentType); + return new(newContext); + } + else if (node is not null) + { + throw new Exception("Delegates cannot be replaced by non-type declarations (enum, class, delegate, etc.)"); + } + + return null; + } + + public void Visit(ContextCSharpSyntaxVisitor visitor) + { + visitor.Visit(Node); + } } internal class TypeContextContainer @@ -2169,6 +2282,14 @@ public void ConvertContainer(TypeContainer? container) Enum = container?.Enum; Delegate = container?.Delegate; } + + public void Delete() + { + Type?.Delete(); + Type = null; + Enum = null; + Delegate = null; + } } private class EnumMemberContext : LeafNodeContext @@ -2189,6 +2310,22 @@ public MethodContext(string ns, MethodDeclarationSyntax node, SyntaxContext cont ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, parentName), pDepth); } + public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) + { + if (parent is not TypeContext ty) + { + return; + } + int pDepth; + Parameters.Clear(); + foreach (var para in Node.ParameterList.Parameters) + { + Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ty)); + } + + ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, ty, out pDepth, parent?.Name ?? string.Empty), pDepth); + } + public Dictionary Parameters = []; public (TypeContextContainer?, int) ReturnType; @@ -2229,6 +2366,11 @@ private class FieldContext : VariableNodes { public FieldContext(TypeContextContainer container, int pointerDepth, BaseFieldDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) + { + Container = context.GetTypeContainer(Node.Declaration.Type, ns, usings, parent as TypeContext, out PointerDepth, parent?.Name ?? string.Empty); + } + public int PointerDepth; public BaseFieldDeclarationSyntax ToCompletedNode() @@ -2250,6 +2392,11 @@ private class PropertyContext : VariableNodes { public PropertyContext(TypeContextContainer container, int pointerDepth, BasePropertyDeclarationSyntax node) : base(container, node) { PointerDepth = pointerDepth; } + public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) + { + Container = context.GetTypeContainer(Node.Type, ns, usings, parent as TypeContext, out PointerDepth, parent?.Name ?? string.Empty); + } + public int PointerDepth; public BasePropertyDeclarationSyntax ToCompletedNode() From 42fec193d468dd22ff9dab2330268da1d3f60f99 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Sun, 21 Jul 2024 15:59:29 -0400 Subject: [PATCH 11/11] First Set of Mods moved to new system Also added to the interfaces and fixed some potential issues related to editing a list within a foreach loop --- .../Clang/ContextCSharpSyntaxRewriter.cs | 40 +- .../Clang/ContextCSharpSyntaxVisitor.cs | 36 +- sources/SilkTouch/Clang/IBaseTypeContext.cs | 33 +- sources/SilkTouch/Clang/IDelegateContext.cs | 11 +- sources/SilkTouch/Clang/IEnumTypeContext.cs | 7 +- sources/SilkTouch/Clang/INamespaceContext.cs | 133 ++ sources/SilkTouch/Clang/SyntaxContext.cs | 1090 +++++++++++------ sources/SilkTouch/Clang/TypeContainer.cs | 6 - sources/SilkTouch/Mods/AddApiProfiles.cs | 524 +------- sources/SilkTouch/Mods/AddOpaqueStructs.cs | 10 +- sources/SilkTouch/Mods/AddVTables.cs | 154 +-- sources/SilkTouch/Mods/ChangeNamespace.cs | 102 +- sources/SilkTouch/Mods/Common/Mod.cs | 2 +- .../Mods/Common/ModCSharpSyntaxRewriter.cs | 3 + 14 files changed, 1188 insertions(+), 963 deletions(-) create mode 100644 sources/SilkTouch/Clang/INamespaceContext.cs diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs index f16060660b..5166b1b7a3 100644 --- a/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxRewriter.cs @@ -1,11 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; namespace Silk.NET.SilkTouch.Clang; @@ -27,13 +23,47 @@ public abstract class ContextCSharpSyntaxRewriter(bool visitIntoStructuredTrivia /// public List Usings { get; internal set; } = []; + /// + /// The current Namespace being visited + /// + public INamespaceContext? CurrentNamespaceContext { get; internal set; } + + /// + /// The top-level namespace context (global context) + /// + public INamespaceContext? TopNamespaceContext { get; internal set; } + /// /// The namespace of the current context /// - public string CurrentNamespace { get; internal set; } = string.Empty; + public string CurrentNamespace => CurrentNamespaceContext?.FullNamespace ?? string.Empty; /// /// The currently SyntaxContext /// public SyntaxContext? Context { get; internal set; } + + /// + /// The file that is currently being editted + /// + public string File { get; internal set; } = string.Empty; + + /// + /// Called when a file is started being worked on + /// + /// + public virtual void OnFileStarted(string fileName) { } + + /// + /// Called when a file is finished being worked on + /// + /// + public virtual void OnFileFinished(string fileName) { } + + /// + /// Whether or not this file should be skipped upon visiting + /// + /// + /// + public virtual bool ShouldSkipFile(string fileName) { return false; } } diff --git a/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs b/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs index 9d9224cbbb..eef5ec0109 100644 --- a/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs +++ b/sources/SilkTouch/Clang/ContextCSharpSyntaxVisitor.cs @@ -25,13 +25,47 @@ public class ContextCSharpSyntaxVisitor : CSharpSyntaxVisitor /// public List Usings { get; internal set; } = []; + /// + /// The current Namespace being visited + /// + public INamespaceContext? CurrentNamespaceContext { get; internal set; } + + /// + /// The top-level namespace context (global context) + /// + public INamespaceContext? TopNamespaceContext { get; internal set; } + /// /// The namespace of the current context /// - public string CurrentNamespace { get; internal set; } = string.Empty; + public string CurrentNamespace => CurrentNamespaceContext?.FullNamespace ?? string.Empty; /// /// The currently SyntaxContext /// public SyntaxContext? Context { get; internal set; } + + /// + /// The file that is currently being editted + /// + public string File { get; internal set; } = string.Empty; + + /// + /// Called when a file is started being worked on + /// + /// + public virtual void OnFileStarted(string fileName) { } + + /// + /// Called when a file is finished being worked on + /// + /// + public virtual void OnFileFinished(string fileName) { } + + /// + /// Whether or not this file should be skipped upon visiting + /// + /// + /// + public virtual bool ShouldSkipFile(string fileName) { return false; } } diff --git a/sources/SilkTouch/Clang/IBaseTypeContext.cs b/sources/SilkTouch/Clang/IBaseTypeContext.cs index 7638ffb090..398932308b 100644 --- a/sources/SilkTouch/Clang/IBaseTypeContext.cs +++ b/sources/SilkTouch/Clang/IBaseTypeContext.cs @@ -1,17 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Silk.NET.SilkTouch.Clang; /// -/// A queryable representation of a +/// A representation of a class, struct, etc. in a SyntaxContext /// public interface IBaseTypeContext { @@ -45,6 +41,11 @@ public interface IBaseTypeContext /// BaseTypeDeclarationSyntax? Node { get; } + /// + /// A list of generic parameters + /// + IEnumerable GenericParameters { get; } + /// /// Attempts to get Type object that is contained within this type /// @@ -52,7 +53,7 @@ public interface IBaseTypeContext /// /// /// - bool TryGetSubType(string typeName, out SubType type, int genericParameterCount = 0); + bool TryGetSubType(string typeName, out TypeContainer type, int genericParameterCount = 0); /// /// Attempts to Add or overwrites a sub type within this type @@ -61,6 +62,13 @@ public interface IBaseTypeContext /// bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + /// + /// Attempts to Add or overwrites a sub type within this type + /// + /// + /// + bool TryAddSubType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + /// /// Removes the subtype with the given name and number of parameters within this type /// @@ -77,7 +85,7 @@ public interface IBaseTypeContext /// /// All subtypes contained within this type /// - IEnumerable<(string, IEnumerable)> SubTypes { get; } + IEnumerable<(string, IEnumerable)> SubTypes { get; } /// /// Attempts to get the type and pointer depth of the field in this type @@ -93,7 +101,7 @@ public interface IBaseTypeContext /// /// bool TryAddField(BaseFieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); - + /// /// Removes the field with the given name within this type /// @@ -269,13 +277,4 @@ public record struct Method(MethodDeclarationSyntax? Node = null, TypeContainer? /// /// public record struct MethodParameter(ParameterSyntax Node, TypeContainer? Type, int TypePointerDepth); - - /// - /// Represents a delegate type - /// - /// - /// - /// - /// - public record Delegate(DelegateDeclarationSyntax? Node = null, TypeContainer? ReturnType = null, int ReturnTypePointerDepth = 0, IEnumerable<(string, MethodParameter)>? Parameters = null); } diff --git a/sources/SilkTouch/Clang/IDelegateContext.cs b/sources/SilkTouch/Clang/IDelegateContext.cs index dc04ad1200..9fcb9b2e17 100644 --- a/sources/SilkTouch/Clang/IDelegateContext.cs +++ b/sources/SilkTouch/Clang/IDelegateContext.cs @@ -1,17 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Silk.NET.SilkTouch.Clang; /// -/// A representation of a Delegate +/// A representation of a Delegate in a SyntaxContext /// public interface IDelegateContext { @@ -30,6 +26,11 @@ public interface IDelegateContext /// IBaseTypeContext? Parent { get; } + /// + /// The number of GenericParameters + /// + int GenericParameterCount { get; } + /// /// A representation of each of the parameters /// diff --git a/sources/SilkTouch/Clang/IEnumTypeContext.cs b/sources/SilkTouch/Clang/IEnumTypeContext.cs index fb2fb19557..bc43ac05c0 100644 --- a/sources/SilkTouch/Clang/IEnumTypeContext.cs +++ b/sources/SilkTouch/Clang/IEnumTypeContext.cs @@ -1,15 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Silk.NET.SilkTouch.Clang; +/// +/// Represents an Enumeration type in a SyntaxContext +/// public interface IEnumTypeContext { /// diff --git a/sources/SilkTouch/Clang/INamespaceContext.cs b/sources/SilkTouch/Clang/INamespaceContext.cs new file mode 100644 index 0000000000..d976f8500f --- /dev/null +++ b/sources/SilkTouch/Clang/INamespaceContext.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Silk.NET.SilkTouch.Clang.IBaseTypeContext; + +namespace Silk.NET.SilkTouch.Clang; + +/// +/// Represents a namespace within a SyntaxContext +/// +public interface INamespaceContext +{ + /// + /// The parent namespace if one exists + /// + INamespaceContext? Parent { get; } + /// + /// The full namespace within this namespace object + /// + string FullNamespace { get; } + /// + /// Name of the current namespace (just the final element) + /// + string Name { get; } + + /// + /// The Syntax node representing this namespace + /// + BaseNamespaceDeclarationSyntax? Node { get; } + + /// + /// Adds the namespace declaration + /// + /// + /// + void AddNamespace(BaseNamespaceDeclarationSyntax syntax, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes a namespace with the given name within this namespace + /// + /// + void RemoveNamespace(string name); + + /// + /// Attempt to get a namespace with the given name within this namespace + /// + /// + /// + /// + bool TryGetNamespace(string name, out INamespaceContext? ns); + + /// + /// All the namespaces contained within this namespace + /// + IEnumerable<(string, INamespaceContext)> Namespaces { get; } + + /// + /// Attempts to get Type object that is contained within this namespace + /// + /// + /// + /// + /// + bool TryGetType(string typeName, out TypeContainer type, int genericParameterCount = 0); + + /// + /// Attempts to Add or overwrites a sub type within this namespace + /// + /// + /// + bool TryAddType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Attempts to Add or overwrites a sub type within this namespace + /// + /// + /// + bool TryAddType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter); + + /// + /// Removes the subtype with the given name and number of parameters within this namespace + /// + /// + /// + void RemoveType(string name, int genericParameterCount = 0); + + /// + /// Removes all subtypes with the given name within this namespace + /// + /// + void RemoveTypes(string name); + + /// + /// All types contained within this namespace + /// + IEnumerable<(string, IEnumerable)> Types { get; } + + /// + /// Merges the members of the given namespace into this one + /// + /// + /// + void Merge(BaseNamespaceDeclarationSyntax ns, ContextCSharpSyntaxRewriter rewriter) + { + foreach (var mem in ns.Members) + { + if (mem is BaseNamespaceDeclarationSyntax subNs) + { + AddNamespace(subNs, rewriter) + } + else if (mem is BaseTypeDeclarationSyntax ty) + { + TryAddType(ty, rewriter); + } + else if (mem is DelegateDeclarationSyntax del) + { + TryAddType(del, rewriter); + } + } + } + + /// + /// Returns a complete version of this node if possible + /// + /// + BaseNamespaceDeclarationSyntax? ToCompletedNode() => null; +} diff --git a/sources/SilkTouch/Clang/SyntaxContext.cs b/sources/SilkTouch/Clang/SyntaxContext.cs index 63ddd671d5..db17ecb347 100644 --- a/sources/SilkTouch/Clang/SyntaxContext.cs +++ b/sources/SilkTouch/Clang/SyntaxContext.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; -using ClangSharp; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -74,7 +73,10 @@ public SyntaxContext(GeneratedSyntax syntax) Dictionary> TypeDefinitionContainers = []; - IReadOnlyList Diagnostics; + /// + /// Set of output diagnostics from ClangSharp + /// + public IReadOnlyList Diagnostics; /// /// Creates a new based on the current state of this Context @@ -95,7 +97,12 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) foreach ((var fName, var context) in Files) { + if (visitor.ShouldSkipFile(fName)) + continue; + visitor.File = fName; + visitor.OnFileStarted(fName); context.Visit(visitor); + visitor.OnFileFinished(fName); } } @@ -111,10 +118,19 @@ public void Rewrite(ContextCSharpSyntaxRewriter rewriter) foreach ((var fName, var context) in Files) { + if (rewriter.ShouldSkipFile(fName)) + continue; + + rewriter.File = fName; + + rewriter.OnFileStarted(fName); + if (!context.Rewrite(rewriter, fName)) { Removals.Add(fName); } + + rewriter.OnFileFinished(fName); } foreach (string rem in Removals) @@ -196,7 +212,7 @@ private void MergeCommonTypes() //We also keep track of all file paths along the way for (int j = 1; j < typeDef.Value.Count; j++) { - if (main.Namespace != typeDef.Value[j].Namespace || + if (main.FullNamespace != typeDef.Value[j].FullNamespace || !TypeVarCheck(main, typeDef.Value[j])) { continue; @@ -243,7 +259,7 @@ private void MergeCommonTypes() //remove the first file path to avoid confusion files.RemoveAt(0); - NamespaceContext ns = Files[mainFile].GetNamespace(main.Namespace); + NamespaceContext ns = Files[mainFile].GetNamespace(main.FullNamespace); //make sure our type actually is in our namespace (can happen if first type in declarations was an UnknownTypeContext which shouldn't happen) if (!ns.Types.TryGetValue(typeDef.Key, out var list)) @@ -255,7 +271,7 @@ private void MergeCommonTypes() { for (int j = 0; j < list.Count; j++) { - if (main.Namespace != list[j].Namespace || + if (main.FullNamespace != list[j].FullNamespace || !TypeVarCheck(main, list[j])) { continue; @@ -283,7 +299,7 @@ private void MergeCommonTypes() } } - private TypeContainer? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, string ns, string file, List usings, IBaseTypeContext? parent = null) + private TypeContainer? CreateTypeContextFromNode(BaseTypeDeclarationSyntax type, INamespaceContext? ns, string file, List usings, IBaseTypeContext? parent = null) { if (type is EnumDeclarationSyntax e) { @@ -318,15 +334,19 @@ private string FindCommonRoot(List paths) private bool TypeVarCheck(TypeContextContainer t1, TypeContextContainer t2) { - if (t1.Type is EnumContext || t2.Type is EnumContext || - t1.Type is not TypeContext ty1 || t2.Type is not TypeContext ty2) + if (t1.Enum is not null || t2.Enum is not null) { return true; } - if (ty1.Node.TypeParameterList is not null && ty2.Node.TypeParameterList is not null && - ty1.Node.TypeParameterList.Parameters.Count != ty2.Node.TypeParameterList.Parameters.Count || - (ty1.Node.ParameterList is null ^ ty2.Node.ParameterList is null)) + if (t1.Delegate is DelegateContext d1 && t2.Delegate is DelegateContext d2 && + (d1.GenericParameterCount != d2.GenericParameterCount)) + { + return false; + } + + if (t1.Type is TypeContext ty1 && t2.Type is TypeContext ty2 && + (ty1.GenericParameterCount != ty2.GenericParameterCount)) { return false; } @@ -338,9 +358,9 @@ private bool TypeVarCheck(TypeContextContainer type, TypeSyntax syntax) { if (syntax is not GenericNameSyntax generic) { - if (type.Type is EnumContext || (type.Type is TypeContext ty && - (ty.Node.TypeParameterList is null || ty.Node.TypeParameterList.Parameters.Count == 0)) || - (type.Type is UnknownTypeContext && type.Type.Syntax is not GenericNameSyntax)) + if (type.Enum is not null || (type.Type is TypeContext ty && + (ty.GenericParameterCount == 0)) || + (type.Delegate is not null && type.Delegate.GenericParameterCount == 0)) { return true; } @@ -351,24 +371,8 @@ private bool TypeVarCheck(TypeContextContainer type, TypeSyntax syntax) return false; } - if (type.Type is TypeContext ty1) - { - if ((ty1.Node.TypeParameterList is not null && - ty1.Node.TypeParameterList.Parameters.Count != generic.TypeArgumentList.Arguments.Count) || - ty1.Node.ParameterList is null) - { - return false; - } - - return true; - } - else if (type.Type is UnknownTypeContext && type.Type.Syntax is GenericNameSyntax generic2) - { - return generic.TypeArgumentList.Arguments.Count == generic2.TypeArgumentList.Arguments.Count; - } - - return false; - + return (type.Type is not null && type.Type.GenericParameterCount == generic.TypeArgumentList.Arguments.Count) || + (type.Delegate is not null && type.Delegate.GenericParameterCount == generic.TypeArgumentList.Arguments.Count); } private void MergeTypeContainers(TypeContextContainer main, List typesToMerge) @@ -439,7 +443,7 @@ private void MergeTypeContainers(TypeContextContainer main, List typesToMerge2 = []; for (int j = 1; j < member.Value.Count; j++) { - if (list[i].Namespace != member.Value[j].Namespace || + if (list[i].FullNamespace != member.Value[j].FullNamespace || !TypeVarCheck(list[i], member.Value[j])) { continue; @@ -584,7 +588,7 @@ private void MergeTypeContainers(TypeContextContainer main, List usings, TypeContext? currentType, out int pDepth, string parentName = "") + private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List usings, TypeContext? currentType, out int pDepth, IEnumerable genericParameters, string parentName = "") { pDepth = 0; while (syn is PointerTypeSyntax pointer) @@ -611,6 +615,11 @@ private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List gen.ToString() == syn.ToString())) + { + return new TypeContextContainer(null, new UnknownTypeContext(syn), SyntaxKind.PublicKeyword); + } + string name = $"{(parentName.Length > 0 ? $"{parentName}." : "")}{currentType?.Node.Identifier.Text ?? string.Empty}"; TypeContextContainer? type = null; @@ -620,7 +629,7 @@ private TypeContextContainer GetTypeContainer(TypeSyntax syn, string ns, List Namespaces = []; + public bool IsWalkingNamespaces = false; + List TempNewNamespaces = []; public CompilationUnitSyntax ToCompletedNode() { @@ -769,6 +780,18 @@ public NamespaceContext GetNamespace(string ns) public int DefinedTypeCount => Namespaces.Select(n => n.Value.DefinedTypeCount).Aggregate((a, b) => a + b); + public INamespaceContext? Parent => null; + + public string FullNamespace => string.Empty; + + public string Name => string.Empty; + + BaseNamespaceDeclarationSyntax? INamespaceContext.Node => null; + + IEnumerable<(string, INamespaceContext)> INamespaceContext.Namespaces => Namespaces.Select(kvp => (kvp.Key, (INamespaceContext)kvp.Value)); + + public IEnumerable<(string, IEnumerable)> Types => Array.Empty<(string, IEnumerable)>(); + public void Clean(SyntaxContext context) { List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); @@ -782,6 +805,10 @@ public void Clean(SyntaxContext context) } else { + if (ns.Value is NamespaceContext nsc) + { + nsc.Parent = this; + } ns.Value.Clean("", context, usings); } } @@ -796,18 +823,48 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) { List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); visitor.Usings = usings; - visitor.Visit(Node); + visitor.TopNamespaceContext = this; foreach (var nameSp in Namespaces) { nameSp.Value.Visit(visitor, usings); } + + visitor.Visit(Node); } public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file) { List usings = Node.Usings.Select(u => u.Name!.ToString()).ToList(); rewriter.Usings = usings; + rewriter.TopNamespaceContext = this; + + List removals = []; + IsWalkingNamespaces = true; + foreach (var nameSp in Namespaces) + { + var output = nameSp.Value.Rewrite(rewriter, "", file); + if (output.Item1 is null) + { + removals.Add(nameSp.Key); + } + + if (output.Item2 is not null) + { + throw new Exception("Type Declarations not allowed in the global namespace"); + } + } + IsWalkingNamespaces = false; + + foreach (string rem in removals) + { + Namespaces.Remove(rem); + } + + Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); + + ReconcileNamespaces(rewriter); + var node = rewriter.Visit(Node) as CompilationUnitSyntax; if (node is null) @@ -820,7 +877,7 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file) { if (member is BaseNamespaceDeclarationSyntax ns) { - var nsContext = new NamespaceContext(string.Empty, ns, rewriter.Context!, rewriter.Usings, file); + var nsContext = new NamespaceContext(string.Empty, ns, rewriter.Context!, rewriter.Usings, this, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else @@ -828,56 +885,89 @@ public bool Rewrite(ContextCSharpSyntaxRewriter rewriter, string file) throw new Exception($"CompilationUnit for {file} contains a member of type ({member.GetType()}) which isn't supported"); } } + Node = node.WithMembers(List(Array.Empty())); - List removals = []; - foreach (var nameSp in Namespaces) + return Namespaces.Count > 0; + } + + public void AddNamespace(BaseNamespaceDeclarationSyntax syntax, ContextCSharpSyntaxRewriter rewriter) + { + if (IsWalkingNamespaces) { - var output = nameSp.Value.Rewrite(rewriter, "", file); - if (output.Item1 is null) - { - removals.Add(nameSp.Key); - } + TempNewNamespaces.Add(syntax); + } - if (output.Item2 is not null) - { - throw new Exception("Type Declarations not allowed in the global namespace"); - } + string name = syntax.Name.ToString(); + if (Namespaces.ContainsKey(name)) + { + var oldNS = Namespaces[name]; + Namespaces[name] = new(rewriter.CurrentNamespace, syntax, rewriter.Context!, rewriter.Usings, this, rewriter.File); + + Namespaces[name].Merge(oldNS); } + else + { + Namespaces.Add(name, new(rewriter.CurrentNamespace, syntax, rewriter.Context!, rewriter.Usings, this, rewriter.File)); + } + } - foreach (string rem in removals) + public void ReconcileNamespaces(ContextCSharpSyntaxRewriter rewriter) + { + foreach(var syntax in TempNewNamespaces) { - Namespaces.Remove(rem); + AddNamespace(syntax, rewriter); } + TempNewNamespaces.Clear(); + } - Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); + public void RemoveNamespace(string name) + { + Namespaces.Remove(name); + } - return Namespaces.Count > 0; + public bool TryGetNamespace(string name, out INamespaceContext? ns) + { + var ret = Namespaces.TryGetValue(name, out var nameSp); + ns = nameSp; + return ret; + } + + public bool TryGetType(string typeName, out TypeContainer type, int genericParameterCount = 0) + { + type = new(); + return false; } + public bool TryAddType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public bool TryAddType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public void RemoveType(string name, int genericParameterCount = 0) { } + public void RemoveTypes(string name) { } } - internal class NamespaceContext + internal class NamespaceContext : INamespaceContext { - public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, SyntaxContext context, List usings, string file = "") + public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, SyntaxContext context, List usings, INamespaceContext? parent, string file = "") { + Parent = parent; string[] names = node.Name.ToString().Split('.'); namesp += $"{(namesp.Length > 0 ? "." : "")}{node.Name}"; if (names.Length != 1) { - NamespaceContext child = new NamespaceContext(namesp, node.WithName(IdentifierName(names[names.Length - 1])), context, usings, file); + NamespaceContext parentNs = new NamespaceContext(namesp, node.WithName(IdentifierName(names[1])), context, usings, this, file); namesp = namesp.Remove(namesp.Length - (names.Last().Length + 1)); node = node.WithMembers(List(Array.Empty())); + Namespaces.Add(names[1], parentNs); - for (int i = names.Length - 2; i >= 1; i--) + for (int i = 2; i >= names.Length; i++) { - NamespaceContext current = new NamespaceContext(namesp, node.WithName(IdentifierName(names[i])), context, usings, file); + NamespaceContext current = new NamespaceContext(namesp, node.WithName(IdentifierName(names[i])), context, usings, parentNs, file); namesp = namesp.Remove(namesp.Length - (names[i].Length + 1)); - current.Namespaces.Add(names[i + 1], child); - child = current; + parentNs.Namespaces.Add(names[i + 1], current); + parentNs = current; } - Namespaces.Add(names[1], child); + Node = node.WithName(IdentifierName(names[0])); return; } @@ -886,12 +976,12 @@ public NamespaceContext(string namesp, BaseNamespaceDeclarationSyntax node, Synt { if (member is BaseNamespaceDeclarationSyntax ns) { - var nsContext = new NamespaceContext(namesp, ns, context, usings, file); + var nsContext = new NamespaceContext(namesp, ns, context, usings, this, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(namesp, context.CreateTypeContextFromNode(bT, namesp, file, usings), bT.Modifiers + var ty = new TypeContextContainer(this, context.CreateTypeContextFromNode(bT, this, file, usings), bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -922,6 +1012,9 @@ public NamespaceContext(BaseNamespaceDeclarationSyntax node) public BaseNamespaceDeclarationSyntax Node; public Dictionary Namespaces = []; public Dictionary> Types = []; + public INamespaceContext? Parent; + List tempNewNodeList = []; + AddLock _CurrentLock = AddLock.None; public BaseNamespaceDeclarationSyntax ToCompletedNode() { @@ -958,18 +1051,14 @@ public NamespaceContext GetNamespace(string ns) public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, string ns = "") { - ns = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; - visitor.CurrentNamespace = ns; - visitor.Visit(Node); - + visitor.CurrentNamespaceContext = this; foreach (var name in Namespaces) { name.Value.Visit(visitor, usings, ns); - visitor.CurrentNamespace = ns; + visitor.CurrentNamespaceContext = this; } - visitor.CurrentNamespace = ns; foreach (var types in Types) { foreach (var type in types.Value) @@ -977,16 +1066,104 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin type.Type?.Visit(visitor); } } + + visitor.Visit(Node); } + public (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { + //Rewrite members first + string names = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; + + List removals = []; + _CurrentLock = AddLock.Namespace; + foreach (var name in Namespaces) + { + var output = name.Value.Rewrite(rewriter, names, file); + if (output.Item1 is null) + { + removals.Add(name.Key); + } + + if (output.Item2 is not null) + { + + if (!Types.TryGetValue(output.Item2.Type!.Name, out var list)) + { + list = []; + Types.Add(output.Item2.Type!.Name, list); + } + + list.Add(output.Item2); + } + } + + foreach (var rem in removals) + { + Namespaces.Remove(rem); + } + + _CurrentLock = AddLock.Type; + ReconcileNamespaces(rewriter); + + Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); + + removals.Clear(); + + + Dictionary> newTypes = []; + foreach (var types in Types) + { + for (var i = 0; i < types.Value.Count; i++) + { + var type = types.Value[i]; + if (type.Type is null) + { + types.Value.RemoveAt(i); + i--; + continue; + } + type.ConvertContainer(type.Type.Rewrite(rewriter, names, file)); + + string name = type.Type!.Name; + if (name != types.Key) + { + if (!Types.TryGetValue(name, out var list) && !newTypes.TryGetValue(name, out list)) + { + list = []; + newTypes.Add(name, list); + } + list.Add(type); + + rewriter.Context!.RenameType(type, types.Key); + } + } + + if (types.Value.Count == 0) + { + removals.Add(types.Key); + } + } + + foreach (var rem in removals) + { + Types.Remove(rem); + } + + foreach (var kvp in newTypes) + { + Types.Add(kvp.Key, kvp.Value); + } + + _CurrentLock = AddLock.None; + ReconcileTypes(rewriter); + + //Rewrite and rectify main node var node = rewriter.Visit(Node); (BaseNamespaceDeclarationSyntax?, TypeContextContainer?) ret; - string names = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; - if (node is BaseNamespaceDeclarationSyntax nameSp) { Node = nameSp; @@ -997,12 +1174,12 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin { if (member is BaseNamespaceDeclarationSyntax subns) { - var nsContext = new NamespaceContext(names, subns, rewriter.Context!, rewriter.Usings, file); + var nsContext = new NamespaceContext(names, subns, rewriter.Context!, rewriter.Usings, this, file); Namespaces.Add(nsContext.Node.Name.ToString(), nsContext); } else if (member is BaseTypeDeclarationSyntax bT) { - var ty = new TypeContextContainer(names, rewriter.Context!.CreateTypeContextFromNode(bT, names, file, rewriter.Usings), bT.Modifiers + var ty = new TypeContextContainer(this, rewriter.Context!.CreateTypeContextFromNode(bT, this, file, rewriter.Usings), bT.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -1026,9 +1203,9 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin else if (node is TypeDeclarationSyntax type) { names = ns; - var cont = rewriter.Context!.CreateTypeContextFromNode(type, ns, file, rewriter.Usings, null); + var cont = rewriter.Context!.CreateTypeContextFromNode(type, rewriter.CurrentNamespaceContext, file, rewriter.Usings, null); - var ty = new TypeContextContainer(ns, cont, type.Modifiers + var ty = new TypeContextContainer(rewriter.CurrentNamespaceContext, cont, type.Modifiers .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) .Select(token => token.Kind()) .FirstOrDefault(SyntaxKind.PrivateKeyword)); @@ -1085,26 +1262,28 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin return (null, null); } + return Namespaces.Count > 0 || Types.Count > 0 ? ret : (null, null); + } + + public void Clean(string ns, SyntaxContext context, List usings) + { + ns = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; List removals = []; - foreach (var name in Namespaces) + + foreach (var nameSp in Namespaces) { - var output = name.Value.Rewrite(rewriter, names, file); - if (output.Item1 is null) + if (nameSp.Value.DefinedTypeCount == 0) { - removals.Add(name.Key); + removals.Add(nameSp.Key); } - - if (output.Item2 is not null) + else { - - if (!Types.TryGetValue(output.Item2.Type!.Name, out var list)) + if (nameSp.Value is NamespaceContext nsc) { - list = []; - Types.Add(output.Item2.Type!.Name, list); + nsc.Parent = this; } - - list.Add(output.Item2); - } + nameSp.Value.Clean(ns, context, usings); + } } foreach (var rem in removals) @@ -1112,34 +1291,19 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin Namespaces.Remove(rem); } - Namespaces = Namespaces.ToDictionary(kvp => kvp.Value.Node.Name.ToString(), kvp => kvp.Value); - removals.Clear(); - Dictionary> newTypes = []; foreach (var types in Types) { - for (var i = 0; i < types.Value.Count; i++) + for (int i = 0; i < types.Value.Count; i++) { - var type = types.Value[i]; - if (type.Type is null) + if (types.Value[i].IsNull) { types.Value.RemoveAt(i); i--; - continue; } - type.ConvertContainer(type.Type.Rewrite(rewriter, names, file)); - - string name = type.Type!.Name; - if (name != types.Key) + else if (types.Value[i].Type is TypeContext ty) { - if (!Types.TryGetValue(name, out var list) && !newTypes.TryGetValue(name, out list)) - { - list = []; - newTypes.Add(name, list); - } - list.Add(type); - - rewriter.Context!.RenameType(type, types.Key); + ty.Clean(ns, context, usings); } } @@ -1153,71 +1317,223 @@ public void Visit(ContextCSharpSyntaxVisitor visitor, List usings, strin { Types.Remove(rem); } + } - foreach (var kvp in newTypes) + public void Merge(NamespaceContext other) + { + foreach (var ns in other.Namespaces) { - Types.Add(kvp.Key, kvp.Value); + if (Namespaces.ContainsKey(ns.Key)) + { + var oldNS = Namespaces[ns.Key]; + Namespaces[ns.Key] = ns.Value; + + ns.Value.Merge(oldNS); + } + else + { + Namespaces.Add(ns.Key, ns.Value); + } } + } - return Namespaces.Count > 0 || Types.Count > 0 ? ret : (null, null); + public void AddNamespace(BaseNamespaceDeclarationSyntax syntax, ContextCSharpSyntaxRewriter rewriter) + { + if (_CurrentLock == AddLock.Namespace) + { + tempNewNodeList.Add(syntax); + } + + string name = syntax.Name.ToString(); + if (Namespaces.ContainsKey(name)) + { + var oldNS = Namespaces[name]; + Namespaces[name] = new(rewriter.CurrentNamespace, syntax, rewriter.Context!, rewriter.Usings, this, rewriter.File); + + Namespaces[name].Merge(oldNS); + } + else + { + Namespaces.Add(name, new(rewriter.CurrentNamespace, syntax, rewriter.Context!, rewriter.Usings, this, rewriter.File)); + } } - public void Clean(string ns, SyntaxContext context, List usings) + public void ReconcileNamespaces(ContextCSharpSyntaxRewriter rewriter) { - ns = $"{(ns.Length > 0 ? $"{ns}." : "")}{Node.Name}"; - List removals = []; + foreach (var syntax in tempNewNodeList) + { + if (syntax is BaseNamespaceDeclarationSyntax ns) + { + AddNamespace(ns, rewriter); + } + } + tempNewNodeList.Clear(); + } - foreach (var nameSp in Namespaces) + public void RemoveNamespace(string name) + { + Namespaces.Remove(name); + } + + public bool TryGetNamespace(string name, out INamespaceContext? ns) + { + var ret = Namespaces.TryGetValue(name, out var nameSp); + ns = nameSp; + return ret; + } + + public bool TryGetType(string typeName, out TypeContainer subType, int genericParameterCount = 0) + { + if (!Types.TryGetValue(typeName, out var value)) { - if (nameSp.Value.DefinedTypeCount == 0) + subType = new(); + return false; + } + + foreach (var container in value) + { + if (container.Type is not null && container.Type.GenericParameterCount == genericParameterCount) { - removals.Add(nameSp.Key); + subType = container.ToTypeContainer(); + return true; } - else + } + + subType = new(); + return false; + } + + public bool TryAddType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + if (_CurrentLock == AddLock.Type) + { + tempNewNodeList.Add(node); + return true; + } + + var ty = new TypeContextContainer(rewriter.CurrentNamespaceContext, rewriter.Context!.CreateTypeContextFromNode(node, rewriter.CurrentNamespaceContext, rewriter.File, rewriter.Usings, null)!, node.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + rewriter.Context?.AddTypeContextContainer(ty); + + if (!Types.TryGetValue(node.Identifier.Text, out var list)) + { + list = [ty]; + Types.Add(node.Identifier.Text, list); + return true; + } + + for (int i = 0; i < list.Count; i++) + { + if (list[i].Type is not null && list[i].Type?.GenericParameterCount == ty.Type?.GenericParameterCount) { - nameSp.Value.Clean(ns, context, usings); - } + list[i] = ty; + return true; + } } + list.Add(ty); + return true; + } - foreach (var rem in removals) + public void ReconcileTypes(ContextCSharpSyntaxRewriter rewriter) + { + foreach (var syntax in tempNewNodeList) { - Namespaces.Remove(rem); + if (syntax is BaseTypeDeclarationSyntax ty) + { + TryAddType(ty, rewriter); + } + else if (syntax is DelegateDeclarationSyntax del) + { + TryAddType(del, rewriter); + } } + tempNewNodeList.Clear(); + } - removals.Clear(); - foreach (var types in Types) + public bool TryAddType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + if (_CurrentLock == AddLock.Type) { - for (int i = 0; i < types.Value.Count; i++) + tempNewNodeList.Add(node); + return true; + } + + var d = new TypeContextContainer(rewriter.CurrentNamespaceContext, new DelegateContext(node, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, null), node.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + rewriter.Context?.AddTypeContextContainer(d); + + if (!Types.TryGetValue(node.Identifier.Text, out var list)) + { + list = [d]; + Types.Add(node.Identifier.Text, list); + return true; + } + + for (int i = 0; i < list.Count; i++) + { + if (list[i].Delegate is not null && list[i].Delegate?.GenericParameterCount == d.Delegate?.GenericParameterCount) + { + list[i] = d; + return true; + } + } + list.Add(d); + return true; + } + public void RemoveType(string name, int genericParameterCount = 0) + { + if (Types.TryGetValue(name, out var list)) + { + for (int i = 0; i < list.Count; i++) { - if (types.Value[i].IsNull) + if (list[i].Type?.GenericParameterCount == genericParameterCount) { - types.Value.RemoveAt(i); + list.RemoveAt(i); i--; } - else if (types.Value[i].Type is TypeContext ty) - { - ty.Clean(ns, context, usings); - } } - if (types.Value.Count == 0) + if (list.Count == 0) { - removals.Add(types.Key); + Types.Remove(name); } } + } - foreach (var rem in removals) - { - Types.Remove(rem); - } + public void RemoveTypes(string name) + { + Types.Remove(name); } public int DefinedTypeCount => Types.Select(t => t.Value.Where(ty => ty.Type is not null).Select(ty => ty.Type is TypeContext type ? type.DefinedTypeCount : 1).Aggregate((a, b) => a + b)).Aggregate((a, b) => a + b) + Namespaces.Select(n => n.Value.DefinedTypeCount).Aggregate((a, b) => a + b); + + INamespaceContext? INamespaceContext.Parent => Parent; + + public string FullNamespace => $"{(Parent is null ? string.Empty : $"{Parent.FullNamespace}.")}{Name}"; + + public string Name => Node.Name.ToString(); + + BaseNamespaceDeclarationSyntax? INamespaceContext.Node => Node; + + IEnumerable<(string, INamespaceContext)> INamespaceContext.Namespaces => Namespaces.Select(kvp => (kvp.Key, (INamespaceContext)kvp.Value)); + + IEnumerable<(string, IEnumerable)> INamespaceContext.Types => Types.Select(kvp => (kvp.Key, kvp.Value.Select(type => type.ToTypeContainer()))); + + enum AddLock : byte + { + None, + Namespace, + Type + } } private class TypeContext : IBaseTypeContext { - public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxContext context, List usings, IBaseTypeContext? parent = null) + public TypeContext(INamespaceContext? ns, string file, TypeDeclarationSyntax node, SyntaxContext context, List usings, IBaseTypeContext? parent = null) { ParentType = parent; File = file; @@ -1227,54 +1543,14 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon foreach (var member in node.Members) { - if (member is BaseTypeDeclarationSyntax bT) - { - var ty = new TypeContextContainer(ns, context.CreateTypeContextFromNode(bT, ns, file, usings, this)!, bT.Modifiers - .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) - .Select(token => token.Kind()) - .FirstOrDefault(SyntaxKind.PrivateKeyword)); - - context.AddTypeContextContainer(ty); - - if (!SubTypes.TryGetValue(bT.Identifier.Text, out var list)) - { - list = []; - SubTypes.Add(bT.Identifier.Text, list); - } - list.Add(ty); - } - else if (member is MethodDeclarationSyntax m) - { - string name = m.Identifier.Text; - if (!Methods.TryGetValue(name, out var methods)) - { - methods = new(); - Methods.Add(name, methods); - } - methods.Add(new(ns, m, context, usings, parentName, this)); - } - else if (member is BaseFieldDeclarationSyntax f) - { - TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); - - foreach (var dec in f.Declaration.Variables) - { - Fields.Add(dec.Identifier.Text, new(type, pDepth, f.WithDeclaration(f.Declaration.WithVariables(SeparatedList(new[] { dec }))))); - } - } - else if (member is PropertyDeclarationSyntax p) - { - TypeContextContainer type = context.GetTypeContainer(p.Type, ns, usings, this, out int pDepth, parentName); - - Properties.Add(p.Identifier.Text, new(type, pDepth, p)); - } + ProcessMember(member, ns, file, context, usings); } if (node.BaseList is not null) { foreach (var baseType in node.BaseList.Types) { - BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, ns, usings, this, out int _)); + BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, ns?.FullNamespace ?? string.Empty, usings, this, out int _, GenericParameters)); } } } @@ -1288,16 +1564,18 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public Dictionary Fields = []; public Dictionary Properties = []; public IBaseTypeContext? ParentType; + List tempNewNodeList = []; + bool _IsWalkingTypes = false; BaseTypeDeclarationSyntax? IBaseTypeContext.Node => Node; IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> IBaseTypeContext.BaseTypes => BaseTypes.Where(bT => bT.Value.Type is not null).Select(bT => (bT.Key, (BaseTypeSyntax)SimpleBaseType(bT.Value.Type!.Syntax), bT.Value.Type!)); - IEnumerable<(string, IEnumerable)> IBaseTypeContext.SubTypes => SubTypes.Select(types => (types.Key, types.Value.Select(type => new IBaseTypeContext.SubType(type.Type?.Node, type.Type)))); + IEnumerable<(string, IEnumerable)> IBaseTypeContext.SubTypes => SubTypes.Select(types => (types.Key, types.Value.Select(type => type.ToTypeContainer()))); - IEnumerable<(string, IBaseTypeContext.Field)> IBaseTypeContext.Fields => Fields.Select(field => (field.Key, new IBaseTypeContext.Field(field.Value.Node, field.Value.Container.ToTypeContainer(), field.Value.PointerDepth))); + IEnumerable<(string, IBaseTypeContext.Field)> IBaseTypeContext.Fields => Fields.Select(field => (field.Key, new IBaseTypeContext.Field(field.Value.Node, field.Value.Container?.ToTypeContainer(), field.Value.PointerDepth))); - IEnumerable<(string, IBaseTypeContext.Property)> IBaseTypeContext.Properties => Properties.Select(property => (property.Key, new IBaseTypeContext.Property(property.Value.Node, property.Value.Container.ToTypeContainer(), property.Value.PointerDepth))); + IEnumerable<(string, IBaseTypeContext.Property)> IBaseTypeContext.Properties => Properties.Select(property => (property.Key, new IBaseTypeContext.Property(property.Value.Node, property.Value.Container?.ToTypeContainer(), property.Value.PointerDepth))); IEnumerable<(string, IEnumerable)> IBaseTypeContext.Methods => Methods.Select(methods => (methods.Key, methods.Value.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.ToTypeContainer(), method.ReturnType.Item2, @@ -1334,6 +1612,7 @@ public TypeContext(string ns, string file, TypeDeclarationSyntax node, SyntaxCon public int GenericParameterCount => Node.TypeParameterList is null ? 0 : Node.TypeParameterList.Parameters.Count; + public IEnumerable GenericParameters => ((IEnumerable?)Node.TypeParameterList?.Parameters ?? Array.Empty()).Concat(Parent?.GenericParameters ?? Array.Empty()); public void Clean(string ns, SyntaxContext context, List usings) { List removals = []; @@ -1386,7 +1665,7 @@ public void Clean(string ns, SyntaxContext context, List usings) } } - public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) + public bool TryGetSubType(string typeName, out TypeContainer subType, int genericParameterCount = 0) { if (!SubTypes.TryGetValue(typeName, out var value)) { @@ -1398,7 +1677,7 @@ public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, { if (container.Type is not null && container.Type.GenericParameterCount == genericParameterCount) { - subType = new(container.Type.Node, container.Type); + subType = container.ToTypeContainer(); return true; } } @@ -1415,7 +1694,7 @@ public bool TryGetField(string fieldName, out IBaseTypeContext.Field field) return false; } - field = new(context.Node, context.Container.ToTypeContainer(), context.PointerDepth); + field = new(context.Node, context.Container?.ToTypeContainer(), context.PointerDepth); return true; } @@ -1427,7 +1706,7 @@ public bool TryGetProperty(string propertyName, out IBaseTypeContext.Property pr return false; } - property = new(context.Node, context.Container.ToTypeContainer(), context.PointerDepth); + property = new(context.Node, context.Container?.ToTypeContainer(), context.PointerDepth); return true; } @@ -1439,9 +1718,7 @@ public bool HasBaseType(string baseType) public void Visit(ContextCSharpSyntaxVisitor visitor) { var oldContext = visitor.CurrentContext; - visitor.CurrentContext = this; - visitor.Visit(Node); foreach (var subTypes in SubTypes) { @@ -1469,139 +1746,140 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) } } + visitor.Visit(Node); + visitor.CurrentContext = oldContext; } public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; - var oldNamespace = rewriter.CurrentNamespace; - rewriter.CurrentContext = new(this); - rewriter.CurrentNamespace = ns; - - var newNode = rewriter.Visit(Node); - - if (newNode is null) - { - rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; - return null; - } - if (newNode is EnumDeclarationSyntax en) + List removals = []; + Dictionary> newTypes = []; + _IsWalkingTypes = true; + foreach (var members in SubTypes) { - var newContext = new TypeContainer(Enum:new EnumContext(file, en, Parent)); + for (int i = 0; i < members.Value.Count; i++) + { + var member = members.Value[i]; + if (member.Type is null) + { + continue; + } + member.ConvertContainer(member.Type.Rewrite(rewriter, ns, file)); - rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; - return newContext; - } - else if (newNode is TypeDeclarationSyntax ty) - { - Node = ty; + if (member.IsNull) + { + members.Value.RemoveAt(i); + i--; + continue; + } - List removals = []; - Dictionary> newTypes = []; - foreach (var members in SubTypes) - { - for (int i = 0; i < members.Value.Count; i++) + if (members.Key != member.Name) { - var member = members.Value[i]; - if (member.Type is null) - { - continue; - } - member.ConvertContainer(member.Type.Rewrite(rewriter, ns, file)); + members.Value.RemoveAt(i); + i--; - if (member.IsNull) + if (SubTypes.TryGetValue(member.Name, out var list2)) { - members.Value.RemoveAt(i); - i--; - continue; + list2.Add(member); } - - if (members.Key != member.Name) + else { - members.Value.RemoveAt(i); - i--; - - if (SubTypes.TryGetValue(member.Name, out var list2)) - { - list2.Add(member); - } - else - { - newTypes.Add(member.Name, [member]); - } - - rewriter.Context!.RenameType(member, members.Key); + newTypes.Add(member.Name, [member]); } - } - if (members.Value.Count == 0) - { - removals.Add(members.Key); - continue; + rewriter.Context!.RenameType(member, members.Key); } } - foreach (var rem in removals) + if (members.Value.Count == 0) { - SubTypes.Remove(rem); + removals.Add(members.Key); + continue; } + } + + foreach (var rem in removals) + { + SubTypes.Remove(rem); + } + + foreach (var newMember in newTypes) + { + SubTypes.Add(newMember.Key, newMember.Value); + } + + _IsWalkingTypes = false; + ReconcileTypes(rewriter); + + List newMembers = new List(); + foreach (var field in Fields) + { + var newMember = rewriter.Visit(field.Value.Node); - foreach (var newMember in newTypes) + if (newMember is MemberDeclarationSyntax member) { - SubTypes.Add(newMember.Key, newMember.Value); + newMembers.Add(member); } + } - List newMembers = new List(); - foreach (var field in Fields) - { - var newMember = rewriter.Visit(field.Value.Node); + foreach (var property in Properties) + { + var newMember = rewriter.Visit(property.Value.Node); - if (newMember is MemberDeclarationSyntax member) - { - newMembers.Add(member); - } + if (newMember is MemberDeclarationSyntax member) + { + newMembers.Add(member); } + } - foreach (var property in Properties) + foreach (var methods in Methods) + { + foreach (var method in methods.Value) { - var newMember = rewriter.Visit(property.Value.Node); + var newMember = rewriter.Visit(method.Node); if (newMember is MemberDeclarationSyntax member) { newMembers.Add(member); } } + } - foreach (var methods in Methods) - { - foreach (var method in methods.Value) - { - var newMember = rewriter.Visit(method.Node); + Methods.Clear(); + Fields.Clear(); + Properties.Clear(); - if (newMember is MemberDeclarationSyntax member) - { - newMembers.Add(member); - } - } - } + foreach (var member in newMembers) + { + ProcessMember(member, rewriter.CurrentNamespaceContext, file, rewriter.Context!, rewriter.Usings); + } - Methods.Clear(); - Fields.Clear(); - Properties.Clear(); + var newNode = rewriter.Visit(Node); - foreach (var member in newMembers) - { - ProcessMember(member, ns, file, rewriter.Context!, rewriter.Usings); - } + if (newNode is null) + { + rewriter.CurrentContext = oldContext; + return null; + } + + if (newNode is EnumDeclarationSyntax en) + { + var newContext = new TypeContainer(Enum:new EnumContext(file, en, Parent)); + + rewriter.CurrentContext = oldContext; + return newContext; + } + else if (newNode is TypeDeclarationSyntax ty) + { + Node = ty; foreach (var member in Node.Members) { - ProcessMember(member, ns, file, rewriter.Context!, rewriter.Usings); + ProcessMember(member, rewriter.CurrentNamespaceContext, file, rewriter.Context!, rewriter.Usings); } if (Node.BaseList is not null) @@ -1619,22 +1897,19 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) var newContext = new TypeContainer(Delegate: new DelegateContext(del, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, Parent)); rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return newContext; } else { rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; throw new Exception("Type Declarations cannot be replaced with non type declarations"); } rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return new(this); } - private void ProcessMember(MemberDeclarationSyntax member, string ns, string file, SyntaxContext context, List usings) + private void ProcessMember(MemberDeclarationSyntax member, INamespaceContext? ns, string file, SyntaxContext context, List usings) { string parentName = Parent is null ? string.Empty : Parent.Name; if (member is BaseTypeDeclarationSyntax bT) @@ -1661,11 +1936,11 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil methods = new(); Methods.Add(name, methods); } - methods.Add(new(ns, m, context, usings, parentName, this)); + methods.Add(new(ns?.FullNamespace ?? string.Empty, m, context, usings, parentName, this)); } else if (member is BaseFieldDeclarationSyntax f) { - TypeContextContainer type = context.GetTypeContainer(f.Declaration.Type, ns, usings, this, out int pDepth, parentName); + TypeContextContainer? type = context.GetTypeContainer(f.Declaration.Type, ns?.FullNamespace ?? string.Empty, usings, this, out int pDepth, GenericParameters, parentName); foreach (var dec in f.Declaration.Variables) { @@ -1674,7 +1949,7 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil } else if (member is BasePropertyDeclarationSyntax p) { - TypeContextContainer type = context.GetTypeContainer(p.Type, ns, usings, this, out int pDepth, parentName); + TypeContextContainer? type = context.GetTypeContainer(p.Type, ns?.FullNamespace ?? string.Empty, usings, this, out int pDepth, GenericParameters, parentName); string propName = "[]"; if (member is PropertyDeclarationSyntax prop) @@ -1687,17 +1962,33 @@ private void ProcessMember(MemberDeclarationSyntax member, string ns, string fil } Properties.Add(propName, new(type, pDepth, p)); } + else if (member is DelegateDeclarationSyntax del) + { + var d = new TypeContextContainer(ns, new DelegateContext(del, ns?.FullNamespace ?? string.Empty, context, usings, this), del.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + + context.AddTypeContextContainer(d); + + if (!SubTypes.TryGetValue(del.Identifier.Text, out var list)) + { + list = []; + SubTypes.Add(del.Identifier.Text, list); + } + list.Add(d); + } } public bool TryAddBaseType(BaseTypeSyntax baseType, SyntaxContext context, ContextCSharpSyntaxRewriter rewriter) { if (BaseTypes.ContainsKey(baseType.Type.ToString())) { - BaseTypes[baseType.Type.ToString()] = context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _); + BaseTypes[baseType.Type.ToString()] = context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _, GenericParameters); } else { - BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _)); + BaseTypes.Add(baseType.Type.ToString(), context.GetTypeContainer(baseType.Type, rewriter.CurrentNamespace, rewriter.Usings, this, out int _, GenericParameters)); } return true; } @@ -1709,7 +2000,13 @@ public void RemoveBaseType(BaseTypeSyntax baseType) public bool TryAddSubType(BaseTypeDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { - ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + if (_IsWalkingTypes) + { + tempNewNodeList.Add(node); + return true; + } + + ProcessMember(node, rewriter.CurrentNamespaceContext, File, rewriter.Context!, rewriter.Usings); return true; } public void RemoveSubType(string name, int genericParameterCount = 0) @@ -1738,7 +2035,7 @@ public void RemoveSubTypes(string name) } public bool TryAddField(BaseFieldDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { - ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + ProcessMember(node, rewriter.CurrentNamespaceContext, File, rewriter.Context!, rewriter.Usings); return true; } public void RemoveField(string name) @@ -1747,7 +2044,7 @@ public void RemoveField(string name) } public bool TryAddProperty(BasePropertyDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { - ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + ProcessMember(node, rewriter.CurrentNamespaceContext, File, rewriter.Context!, rewriter.Usings); return true; } public void RemoveProperty(string name) @@ -1767,12 +2064,12 @@ public bool TryGetMethods(string name, out IEnumerable return false; } methodInfo = methods.Select(method => new IBaseTypeContext.Method(method.Node, method.ReturnType.Item1?.ToTypeContainer(), method.ReturnType.Item2, - method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.ToTypeContainer(), par.Value.PointerDepth))))); + method.Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type?.ToTypeContainer(), par.Value.PointerDepth))))); return true; } public bool TryAddMethod(MethodDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) { - ProcessMember(node, rewriter.CurrentNamespace, File, rewriter.Context!, rewriter.Usings); + ProcessMember(node, rewriter.CurrentNamespaceContext, File, rewriter.Context!, rewriter.Usings); return true; } public void RemoveMethod(string name, params TypeSyntax[] parameters) @@ -1810,6 +2107,64 @@ public void Delete() } } } + + public bool TryAddSubType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) + { + if (_IsWalkingTypes) + { + tempNewNodeList.Add(node); + return true; + } + var d = new TypeContextContainer(rewriter.CurrentNamespaceContext, new DelegateContext(node, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, this), node.Modifiers + .Where(token => token.IsKind(SyntaxKind.PublicKeyword) || token.IsKind(SyntaxKind.ProtectedKeyword) || token.IsKind(SyntaxKind.PrivateKeyword)) + .Select(token => token.Kind()) + .FirstOrDefault(SyntaxKind.PrivateKeyword)); + + rewriter.Context?.AddTypeContextContainer(d); + + if (!SubTypes.TryGetValue(node.Identifier.Text, out var list)) + { + list = [ d ]; + SubTypes.Add(node.Identifier.Text, list); + return true; + } + + for (int i = 0; i < list.Count; i ++) + { + if (list[i].Delegate is not null && list[i].Delegate?.GenericParameterCount == d.Delegate?.GenericParameterCount) + { + list[i] = d; + return true; + } + } + list.Add(d); + return true; + } + + public void ReconcileTypes(ContextCSharpSyntaxRewriter rewriter) + { + foreach (var syntax in tempNewNodeList) + { + if (syntax is BaseTypeDeclarationSyntax ty) + { + TryAddSubType(ty, rewriter); + } + else if (syntax is DelegateDeclarationSyntax del) + { + TryAddSubType(del, rewriter); + } + } + tempNewNodeList.Clear(); + } + + enum AddLock + { + None, + Type, + Method, + Property, + Field + } } private class EnumContext : IEnumTypeContext @@ -1834,6 +2189,9 @@ public EnumContext(string file, EnumDeclarationSyntax node, IBaseTypeContext? pa public Dictionary Members = []; + bool IsWalkingEnumMembers = false; + List tempNewNodesList = []; + EnumDeclarationSyntax? IEnumTypeContext.Node => Node; public IEnumerable<(string, EnumMemberDeclarationSyntax)> EnumMembers => Members.Select(em => (em.Key, em.Value.Node)); @@ -1862,17 +2220,38 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; - var oldNamespace = rewriter.CurrentNamespace; - rewriter.CurrentContext = new(Enum:this); - rewriter.CurrentNamespace = ns; + + IsWalkingEnumMembers = true; + List removals = []; + foreach (var member in Members) + { + var node = rewriter.Visit(member.Value.Node); + + if (node is not EnumMemberDeclarationSyntax em) + { + removals.Add(member.Key); + continue; + } + + member.Value.Node = em; + } + IsWalkingEnumMembers = false; + + foreach (var rem in removals) + { + Members.Remove(rem); + } + + ReconcileEnumMembers(); + + Members = Members.ToDictionary(kvp => kvp.Value.Node.Identifier.Text, kvp => kvp.Value); var newNode = rewriter.Visit(Node); if (newNode is null) { rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return null; } @@ -1880,28 +2259,6 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) { Node = en; - List removals = []; - foreach(var member in Members) - { - var node = rewriter.Visit(member.Value.Node); - - if (node is not EnumMemberDeclarationSyntax em) - { - removals.Add(member.Key); - continue; - } - - member.Value.Node = em; - } - - foreach(var rem in removals) - { - Members.Remove(rem); - } - - Members = Members.ToDictionary(kvp => kvp.Value.Node.Identifier.Text, kvp => kvp.Value); - - foreach (var member in Node.Members) { if (Members.ContainsKey(member.Identifier.Text)) @@ -1918,10 +2275,9 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) } else if (newNode is TypeDeclarationSyntax ty) { - var newContext = new TypeContext(ns, file, ty, rewriter.Context!, rewriter.Usings, ParentType); + var newContext = new TypeContext(rewriter.CurrentNamespaceContext, file, ty, rewriter.Context!, rewriter.Usings, ParentType); rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return new(newContext); } else if (newNode is DelegateDeclarationSyntax del) @@ -1929,7 +2285,6 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) var newContext = new TypeContainer(Delegate: new DelegateContext(del, rewriter.CurrentNamespace, rewriter.Context!, rewriter.Usings, ParentType)); rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return newContext; } else @@ -1939,7 +2294,6 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) var originalContext = rewriter.CurrentContext; rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return originalContext; } @@ -1968,6 +2322,12 @@ public bool HasBaseType(BaseTypeSyntax baseType) public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) { + if (IsWalkingEnumMembers) + { + tempNewNodesList.Add(node); + return true; + } + if (Members.ContainsKey(node.Identifier.Text)) { Members[node.Identifier.Text] = new(node); @@ -1978,6 +2338,15 @@ public bool TryAddEnumMember(EnumMemberDeclarationSyntax node) } return true; } + + public void ReconcileEnumMembers() + { + foreach (var node in tempNewNodesList) + { + TryAddEnumMember(node); + } + } + public void RemoveEnumMember(string name) { Members.Remove(name); @@ -1986,6 +2355,7 @@ public void RemoveEnumMember(string name) public void SetParent(IBaseTypeContext? parent) => ParentType = parent; } + private class UnknownTypeContext : IBaseTypeContext { public UnknownTypeContext(TypeSyntax type) @@ -2008,7 +2378,7 @@ public UnknownTypeContext(TypeSyntax type) public IEnumerable<(string, BaseTypeSyntax, IBaseTypeContext)> BaseTypes => Array.Empty<(string, BaseTypeSyntax, IBaseTypeContext)>(); - public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); + public IEnumerable<(string, IEnumerable)> SubTypes => Array.Empty<(string, IEnumerable)>(); public IEnumerable<(string, IBaseTypeContext.Field)> Fields => Array.Empty<(string, IBaseTypeContext.Field)>(); @@ -2016,6 +2386,8 @@ public UnknownTypeContext(TypeSyntax type) public IEnumerable<(string, IEnumerable)> Methods => Array.Empty<(string, IEnumerable)>(); + public IEnumerable GenericParameters => Parent?.GenericParameters ?? Array.Empty(); + public MemberDeclarationSyntax? ToCompletedNode() { return null; @@ -2029,27 +2401,23 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) public TypeContainer? Rewrite(ContextCSharpSyntaxRewriter rewriter, string ns, string file) { var oldContext = rewriter.CurrentContext; - var oldNamespace = rewriter.CurrentNamespace; rewriter.CurrentContext = new(this); - rewriter.CurrentNamespace = ns; var type = rewriter.Visit(Type) as TypeSyntax; if (type is not null) { rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return new(new UnknownTypeContext(type)); } rewriter.CurrentContext = oldContext; - rewriter.CurrentNamespace = oldNamespace; return null; } - public bool TryGetSubType(string typeName, out IBaseTypeContext.SubType subType, int genericParameterCount = 0) + public bool TryGetSubType(string typeName, out TypeContainer subType, int genericParameterCount = 0) { subType = new(); return false; @@ -2089,6 +2457,8 @@ public void SetParent(IBaseTypeContext? parent) { } public void Delete() { } + public bool TryAddSubType(DelegateDeclarationSyntax node, ContextCSharpSyntaxRewriter rewriter) => false; + public enum TypeLocation { BaseList, @@ -2109,7 +2479,7 @@ public DelegateContext(DelegateDeclarationSyntax node, string ns, SyntaxContext Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ParentType as TypeContext)); } - ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.Name ?? string.Empty), pDepth); + ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.GenericParameters ?? Array.Empty(), parent?.Name ?? string.Empty), pDepth); } public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) @@ -2121,7 +2491,7 @@ public void RefreshTypeLinks(string ns, SyntaxContext context, List usin Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ParentType as TypeContext)); } - ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.Name ?? string.Empty), pDepth); + ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, parent as TypeContext, out pDepth, parent?.GenericParameters ?? Array.Empty(), parent?.Name ?? string.Empty), pDepth); } public Dictionary Parameters = []; @@ -2132,9 +2502,11 @@ public void RefreshTypeLinks(string ns, SyntaxContext context, List usin public string Name => $"{(ParentType is null ? "" : $"{ParentType.Name}.")}{Node.Identifier.Text}"; - DelegateDeclarationSyntax? IDelegateContext.Node => throw new NotImplementedException(); + DelegateDeclarationSyntax? IDelegateContext.Node => Node; - IEnumerable<(string, IBaseTypeContext.MethodParameter)> IDelegateContext.Parameters => Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type.ToTypeContainer(), par.Value.PointerDepth))); + IEnumerable<(string, IBaseTypeContext.MethodParameter)> IDelegateContext.Parameters => Parameters.Select(par => (par.Key, new IBaseTypeContext.MethodParameter(par.Value.Node, par.Value.Type?.ToTypeContainer(), par.Value.PointerDepth))); + + public int GenericParameterCount => Node.TypeParameterList?.Parameters.Count ?? 0; public static bool operator ==(DelegateContext left, DelegateContext right) { @@ -2179,7 +2551,7 @@ public override string ToString() } else if (node is TypeDeclarationSyntax ty) { - var newContext = new TypeContext(ns, file, ty, rewriter.Context!, rewriter.Usings, ParentType); + var newContext = new TypeContext(rewriter.CurrentNamespaceContext, file, ty, rewriter.Context!, rewriter.Usings, ParentType); return new(newContext); } else if (node is not null) @@ -2198,7 +2570,7 @@ public void Visit(ContextCSharpSyntaxVisitor visitor) internal class TypeContextContainer { - public TypeContextContainer(string ns, IBaseTypeContext? ty, IEnumTypeContext? en, IDelegateContext? del, SyntaxKind visibility) + public TypeContextContainer(INamespaceContext? ns, IBaseTypeContext? ty, IEnumTypeContext? en, IDelegateContext? del, SyntaxKind visibility) { Namespace = ns; Type = ty; @@ -2207,7 +2579,7 @@ public TypeContextContainer(string ns, IBaseTypeContext? ty, IEnumTypeContext? e Enum = en; } - public TypeContextContainer(string ns, TypeContainer? container, SyntaxKind visibility) + public TypeContextContainer(INamespaceContext? ns, TypeContainer? container, SyntaxKind visibility) { Namespace = ns; Type = container?.Type; @@ -2216,7 +2588,7 @@ public TypeContextContainer(string ns, TypeContainer? container, SyntaxKind visi Visibility = visibility; } - public TypeContextContainer(string ns, IBaseTypeContext? ty, SyntaxKind visibility) + public TypeContextContainer(INamespaceContext? ns, IBaseTypeContext? ty, SyntaxKind visibility) { Namespace = ns; Type = ty; @@ -2225,7 +2597,7 @@ public TypeContextContainer(string ns, IBaseTypeContext? ty, SyntaxKind visibili Enum = null; } - public TypeContextContainer(string ns, IEnumTypeContext? en, SyntaxKind visibility) + public TypeContextContainer(INamespaceContext? ns, IEnumTypeContext? en, SyntaxKind visibility) { Namespace = ns; Type = null; @@ -2234,7 +2606,7 @@ public TypeContextContainer(string ns, IEnumTypeContext? en, SyntaxKind visibili Enum = en; } - public TypeContextContainer(string ns, IDelegateContext? del, SyntaxKind visibility) + public TypeContextContainer(INamespaceContext? ns, IDelegateContext? del, SyntaxKind visibility) { Namespace = ns; Type = null; @@ -2243,7 +2615,8 @@ public TypeContextContainer(string ns, IDelegateContext? del, SyntaxKind visibil Enum = null; } - public string Namespace; + public INamespaceContext? Namespace; + public string FullNamespace => Namespace?.FullNamespace ?? string.Empty; public SyntaxKind Visibility; public IBaseTypeContext? Type; public IEnumTypeContext? Enum; @@ -2307,7 +2680,7 @@ public MethodContext(string ns, MethodDeclarationSyntax node, SyntaxContext cont Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parentName, type)); } - ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, parentName), pDepth); + ReturnType = (context.GetTypeContainer(node.ReturnType, ns, usings, type, out pDepth, type?.GenericParameters ?? Array.Empty(), parentName), pDepth); } public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) @@ -2323,7 +2696,7 @@ public void RefreshTypeLinks(string ns, SyntaxContext context, List usin Parameters.Add(para.Identifier.Text, new(para, ns, context, usings, parent?.Name ?? string.Empty, ty)); } - ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, ty, out pDepth, parent?.Name ?? string.Empty), pDepth); + ReturnType = (context.GetTypeContainer(Node.ReturnType, ns, usings, ty, out pDepth, parent?.GenericParameters ?? Array.Empty(), parent?.Name ?? string.Empty), pDepth); } public Dictionary Parameters = []; @@ -2355,7 +2728,7 @@ private class MethodParameterContext : LeafNodeContext { public MethodParameterContext(ParameterSyntax node, string ns, SyntaxContext context, List usings, string parentName, TypeContext? type) : base(node) { - Type = context.GetTypeContainer(node.Type!, ns, usings, type, out PointerDepth, parentName); + Type = context.GetTypeContainer(node.Type!, ns, usings, type, out PointerDepth, type?.GenericParameters ?? Array.Empty(), parentName); } public TypeContextContainer Type; @@ -2368,13 +2741,22 @@ private class FieldContext : VariableNodes public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) { - Container = context.GetTypeContainer(Node.Declaration.Type, ns, usings, parent as TypeContext, out PointerDepth, parent?.Name ?? string.Empty); + if (parent is TypeContext type) + { + var genericTypes = type.GenericParameters; + + Container = context.GetTypeContainer(Node.Declaration.Type, ns, usings, type, out PointerDepth, parent?.GenericParameters ?? Array.Empty(), parent?.Name ?? string.Empty); + } } public int PointerDepth; public BaseFieldDeclarationSyntax ToCompletedNode() { + if (Container is null) + { + return Node; + } var type = Container.Type!.Syntax; int pDepth = PointerDepth; @@ -2394,13 +2776,20 @@ private class PropertyContext : VariableNodes public void RefreshTypeLinks(string ns, SyntaxContext context, List usings, IBaseTypeContext? parent) { - Container = context.GetTypeContainer(Node.Type, ns, usings, parent as TypeContext, out PointerDepth, parent?.Name ?? string.Empty); + if (parent is TypeContext type) + { + Container = context.GetTypeContainer(Node.Type, ns, usings, type, out PointerDepth, parent?.GenericParameters ?? Array.Empty(), parent?.Name ?? string.Empty); + } } public int PointerDepth; public BasePropertyDeclarationSyntax ToCompletedNode() { + if (Container is null) + { + return Node; + } var type = Container.Type!.Syntax; int pDepth = PointerDepth; @@ -2417,7 +2806,10 @@ public BasePropertyDeclarationSyntax ToCompletedNode() private class VariableNodes : LeafNodeContext where TNodeType : SyntaxNode { - public VariableNodes(TypeContextContainer container, TNodeType node) : base(node) { Container = container; } + public VariableNodes(TypeContextContainer container, TNodeType node) : base(node) + { + Container = container; + } public TypeContextContainer Container; } diff --git a/sources/SilkTouch/Clang/TypeContainer.cs b/sources/SilkTouch/Clang/TypeContainer.cs index e2c5ce1c47..65c888ee4e 100644 --- a/sources/SilkTouch/Clang/TypeContainer.cs +++ b/sources/SilkTouch/Clang/TypeContainer.cs @@ -1,12 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Silk.NET.SilkTouch.Clang; /// diff --git a/sources/SilkTouch/Mods/AddApiProfiles.cs b/sources/SilkTouch/Mods/AddApiProfiles.cs index f1125f50c4..96eea5a62d 100644 --- a/sources/SilkTouch/Mods/AddApiProfiles.cs +++ b/sources/SilkTouch/Mods/AddApiProfiles.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading.Tasks; using Humanizer; @@ -61,11 +62,11 @@ class Rewriter( string? jobKey, IEnumerable< IApiMetadataProvider> - > versionProviders + > versionProviders, + Dictionary aggregatedUsings, + Configuration cfg ) : ModCSharpSyntaxRewriter { - public BakeSet? Baked { get; set; } - public ApiProfileDecl? Profile { get; set; } public ILogger? Logger { get; set; } @@ -287,7 +288,7 @@ ConversionOperatorDeclarationSyntax node where T : BaseFieldDeclarationSyntax { var retNode = @base(node); - if (Baked is null && Profile is not null && retNode is T ret) + if (Profile is not null && retNode is T ret) { // When we're in baking mode, all of the baking logic is done in VisitVariableDeclarator. However this // does nothing in the non-baking case, where we should add the attribute to the field as a marker. @@ -321,75 +322,11 @@ ConversionOperatorDeclarationSyntax node return retNode; } - if (Baked is null) - { - return retNode is T ret + return retNode is T ret ? ret.AddAttributeLists( AttributeList(SingletonSeparatedList(GetProfileAttribute(null))) ) : retNode; - } - - // Get this type's bake set for some final validation checks. - // This also registers the bake set if the type had no members. - var bakeSet = GetOrRegisterTypeBakeSet(node); - - // If this is a struct or record struct, fields should exactly match. - if ( - ( - node is StructDeclarationSyntax s - ? (SyntaxList?)s.Members - : node is RecordDeclarationSyntax r - && r.Modifiers.Any(SyntaxKind.StructKeyword) - ? r.Members - : null - ) - is not { } members - ) - { - // If it's not a struct or record struct, we've registered the type and that's all we really need to do. - // Any members will have been registered in the base call. - return null; // baking erases, but the caller should know that. - } - - var bakedFields = bakeSet - .Children.OrderBy(x => x.Value.Index) - .Select(x => x.Value.Syntax) - .OfType() - .SelectMany(x => x.Declaration.Variables.Select(y => (x.Declaration.Type, Var: y))) - .ToArray(); - var ourFields = members - .OfType() - .SelectMany(x => x.Declaration.Variables.Select(y => (x.Declaration.Type, Var: y))) - .ToArray(); - if (bakedFields.Length != ourFields.Length) - { - throw new InvalidOperationException( - $"Differing number of fields between definitions of {node.Identifier} ({bakedFields.Length} vs " - + $"{ourFields.Length})" - ); - } - - for (var i = 0; i < bakedFields.Length; i++) - { - if ( - bakedFields[i].Type.ToString() != ourFields[i].Type.ToString() - || bakedFields[i].Var.Identifier.ToString() - != ourFields[i].Var.Identifier.ToString() - || bakedFields[i].Var.Initializer?.ToString() - != ourFields[i].Var.Initializer?.ToString() - || bakedFields[i].Var.ArgumentList?.ToString() - != ourFields[i].Var.ArgumentList?.ToString() - ) - { - throw new InvalidOperationException( - $"Field {i} differs between definitions of {node.Identifier}. " - + $"Left: {bakedFields[i]}, right: {ourFields[i]}" - ); - } - } - - return null; // baking erases, but the caller should know that. } private SyntaxNode? Visit( @@ -425,294 +362,55 @@ is not { } members return retNode; } - // If we're not in baking mode... - if (Baked is null) + // If we didn't get an inner node when we're not in baking mode, return now as this is bad. + // Also, we can only add attributes for TSeparated, not the inner TVisiting, so we also return now if we + // want to add an attribute. This is super convoluted I know. The onus is on the caller to add the + // attribute in this case. + if (retNode is not (TSeparated ret and TVisiting)) { - // If we didn't get an inner node when we're not in baking mode, return now as this is bad. - // Also, we can only add attributes for TSeparated, not the inner TVisiting, so we also return now if we - // want to add an attribute. This is super convoluted I know. The onus is on the caller to add the - // attribute in this case. - if (retNode is not (TSeparated ret and TVisiting)) - { - // expected to at least be TVisiting, but we can only add if it's TSeparated. If it's not at least - // TVisiting, it is likely null. In either case this does what we want. - return retNode; - } - - // Add the attribute if this is the node we are visiting. - return ret.AddAttributeLists( - AttributeList(SingletonSeparatedList(GetProfileAttribute(name))) - ); + // expected to at least be TVisiting, but we can only add if it's TSeparated. If it's not at least + // TVisiting, it is likely null. In either case this does what we want. + return retNode; } - Logger?.LogTrace( - "Baking item for \"{}\" with discriminator \"{}\": {}", - Profile.Profile, - discrim, - nodeToAdd + // Add the attribute if this is the node we are visiting. + return ret.AddAttributeLists( + AttributeList(SingletonSeparatedList(GetProfileAttribute(name))) ); - var parent = GetOrRegisterAncestorBakeSet(nodeVisiting); + } - // Have we seen the member before? - if (parent.Children.TryGetValue(discrim, out var baked)) + public override bool ShouldSkipFile(string fileName) + { + if (!fileName.StartsWith("sources/")) { - // Make sure it's the same concrete type - if (baked.Syntax.GetType() != nodeToAdd.GetType()) - { - throw new InvalidOperationException( - $"The existing definition for \"{discrim}\" is a {baked.Syntax.GetType()} whereas this " - + $"definition is a {nodeToAdd.GetType()}. Left: {baked.Syntax}, right: {nodeToAdd}" - ); - } - - // Boldly assume it's the same implementation, modifiers, etc - // TODO ^^^ is this okay? should be fine if we're getting DllImports as inputs. - - // Okay fine here's some extra handling just in case it's a partial: - var bakedNode = (TSeparated)baked.Syntax; - if ( - nodeToAdd is BaseMethodDeclarationSyntax meth - && bakedNode is BaseMethodDeclarationSyntax bakedMeth - && baked.Syntax.Modifiers.Any(SyntaxKind.PartialKeyword) - && nodeToAdd.Modifiers.Any(SyntaxKind.PartialKeyword) - ) - { - var precedence = false; - if (bakedMeth.Body is null && meth.Body is not null) - { - if (bakedMeth.ExpressionBody is not null) - { - throw new InvalidOperationException( - $"The existing definition for \"{discrim}\" provides an expression body whereas this " - + "definition provides a statement body" - ); - } - - // Our definition takes precedence - precedence = true; - } - - if (bakedMeth.ExpressionBody is null && meth.ExpressionBody is not null) - { - if (bakedMeth.Body is not null) - { - throw new InvalidOperationException( - $"The existing definition for \"{discrim}\" provides an expression body whereas this " - + "definition provides a statement body" - ); - } - - if (precedence) - { - throw new InvalidOperationException( - $"This definition for \"{discrim}\" provides both an expression and a statement body." - ); - } - - // Our definition takes precedence - precedence = true; - } - - if (precedence) - { - baked.Syntax = nodeToAdd.AddAttributeLists( - baked - .Syntax.AttributeLists.Select(x => - x.WithAttributes( - SeparatedList( - x.Attributes.Where(y => - y.IsAttribute("Silk.NET.Core.SupportedApiAttribute") - ) - ) - ) - ) - .Where(x => x.Attributes.Count > 0) - .ToArray() - ); - } - } - - // Check that constants and enums have the same value - if ( - (baked.Syntax, nodeToAdd) is - - (EnumMemberDeclarationSyntax lEnum, EnumMemberDeclarationSyntax rEnum) - ) - { - if (lEnum.EqualsValue?.Value.ToString() != rEnum.EqualsValue?.Value.ToString()) - { - Logger?.LogWarning( - "Enum member with discriminator \"{}\" differs between definitions. Left: {}, right: {}", - discrim, - lEnum.EqualsValue?.Value.ToString() ?? "auto-assigned", - rEnum.EqualsValue?.Value.ToString() ?? "auto-assigned" - ); - } - } - else if ( - (baked.Syntax, nodeToAdd) is + return true; + } - (FieldDeclarationSyntax lConst, FieldDeclarationSyntax rConst) + Profile = cfg + .Profiles?.Where(x => + fileName[8..].StartsWith(x.SourceSubdirectory, StringComparison.OrdinalIgnoreCase) ) - { - var isConst = lConst.Modifiers.Any(SyntaxKind.ConstKeyword); - if (isConst != rConst.Modifiers.Any(SyntaxKind.ConstKeyword)) - { - Logger?.LogWarning( - "Const with discriminator \"{}\" isn't const in its redefinition. Left: {}, right: {}", - discrim, - lConst.ToString(), - rConst.ToString() - ); - } - else if ( - isConst - && lConst.Declaration.Variables[0].Initializer?.Value.ToString() - != rConst.Declaration.Variables[0].Initializer?.Value.ToString() - ) - { - Logger?.LogWarning( - "Const value with discriminator \"{}\" differs between definitions. Left: {}, right: {}", - discrim, - lConst.Declaration.Variables[0].Initializer?.Value.ToString(), - rConst.Declaration.Variables[0].Initializer?.Value.ToString() - ); - } - } + .MaxBy(x => x.SourceSubdirectory.Length); + if (Profile is null) + { + return true; } - - // Update the bake set. This adds if we haven't seen the member before, otherwise we just update the - // existing declaration by adding our attribute list. - parent.Children[discrim] = ( - (baked.Syntax ?? nodeToAdd).AddAttributeLists( - AttributeList(SingletonSeparatedList(GetProfileAttribute(name))) - ), - null, - baked.Syntax is null ? parent.Children.Count : baked.Index - ); - - return null; // erase it, but the caller should know to do that anyway. + return false; } - private BakeSet GetOrRegisterAncestorBakeSet(SyntaxNode node) => - node.Parent is null - ? Baked ?? throw new InvalidOperationException("BakeSet not set") - : GetOrRegisterTypeBakeSet(node.Parent); - - private BakeSet GetOrRegisterTypeBakeSet(SyntaxNode node) + public override void OnFileStarted(string fileName) { - var nsPre = node.NamespaceFromSyntaxNode() is { Length: > 0 } ns - ? $"{ns}." - : string.Empty; - var bakeSet = Baked ?? throw new InvalidOperationException("BakeSet not set"); - - // To handle nested types, we go through each ancestor (starting from the top, hence the reverse) and get - // the bake set (or create if needed), and do this for each ancestor until we finally get to the containing - // type of the given node. - foreach ( - var decl in node.AncestorsAndSelf().OfType().Reverse() - ) - { - var discrim = $"{nsPre}{decl.Identifier}"; - if ( - decl is TypeDeclarationSyntax - { - TypeParameterList.Parameters.Count: > 0 and var cnt - } - ) - { - discrim += $"`{cnt}"; - } - nsPre = string.Empty; // only the top-level type shall be prefixed with the namespace - bakeSet = ( - bakeSet!.Children.TryGetValue(discrim, out var baked) - ? bakeSet.Children[discrim] = ( - MergeDecls( - WithProfile(StripBare(decl), Profile), - (BaseTypeDeclarationSyntax)baked.Syntax - ), - baked.Inner ?? new BakeSet(), - baked.Index - ) - : bakeSet.Children[discrim] = ( - WithProfile(StripBare(decl), Profile), - new BakeSet(), - bakeSet.Children.Count - ) - ).Inner; - } + Logger?.LogDebug("Identified profile {} for {}", Profile, fileName); + } - return bakeSet!; - static BaseTypeDeclarationSyntax StripBare(BaseTypeDeclarationSyntax node) => - node switch - { - // TODO do we need to strip more than this for dedupe purposes? - TypeDeclarationSyntax klass => klass.WithMembers(default), - EnumDeclarationSyntax enumeration => enumeration.WithMembers(default), - _ => node - }; - - static BaseTypeDeclarationSyntax MergeDecls( - BaseTypeDeclarationSyntax node1, - BaseTypeDeclarationSyntax node2 - ) + public override void OnFileFinished(string fileName) + { + foreach (var (k, v) in UsingsToAdd) { - if (node1.GetType() != node2.GetType()) - { - throw new ArgumentException( - "Node types differed - the profiles may contain two types with the " - + "same name but with a different datatype (i.e. profile 1 contains a " - + $"{node1.Kind().Humanize()} whereas profile 2 contains a {node2.Kind().Humanize()})" - ); - } - return node1 - .WithModifiers( - TokenList( - node2.Modifiers.Concat(node2.Modifiers).DistinctBy(x => x.ToString()) - ) - ) - .WithBaseList( - node1.BaseList?.WithTypes( - SeparatedList( - node1 - .BaseList.Types.Concat( - node2.BaseList?.Types ?? Enumerable.Empty() - ) - .DistinctBy(x => x.ToString()) - ) - ) - ) - .WithAttributeLists( - List( - node1 - .AttributeLists.SelectMany(x => - x.Attributes.Select(y => - x.WithAttributes(SingletonSeparatedList(y)) - ) - ) - .Concat( - node2.AttributeLists.SelectMany(x => - x.Attributes.Select(y => - x.WithAttributes(SingletonSeparatedList(y)) - ) - ) - ) - .DistinctBy(x => x.ToString()) - ) - ); + aggregatedUsings.TryAdd(k, v); } - - BaseTypeDeclarationSyntax WithProfile( - BaseTypeDeclarationSyntax decl, - ApiProfileDecl? profile - ) => - profile is null - ? decl - : decl.AddAttributeLists( - AttributeList( - SingletonSeparatedList(GetProfileAttribute(decl.Identifier.ToString())) - ) - ); + UsingsToAdd.Clear(); + Profile = null; } } @@ -724,147 +422,15 @@ public Dictionary< > Children { get; } = new(); } - /// - /// A map of baked roots to deduplicated es and their associated discriminants. - /// - private Dictionary _baked = new(); - /// - public override Task AfterScrapeAsync(string key, GeneratedSyntax syntax) + public override Task AfterScrapeAsync(string key, SyntaxContext context) { var cfg = config.Get(key); - var rewriter = new Rewriter(key, versionProviders.Get(key).ToArray()) { Logger = logger }; - var bakery = new Dictionary(); - var baked = new List(); var aggregatedUsings = new Dictionary(); - foreach (var (path, root) in syntax.Files) - { - if (!path.StartsWith("sources/")) - { - continue; - } + var rewriter = new Rewriter(key, versionProviders.Get(key).ToArray(), aggregatedUsings, cfg) { Logger = logger }; - rewriter.Profile = cfg - .Profiles?.Where(x => - path[8..].StartsWith(x.SourceSubdirectory, StringComparison.OrdinalIgnoreCase) - ) - .MaxBy(x => x.SourceSubdirectory.Length); - if (rewriter.Profile is null) - { - continue; - } - - logger.LogDebug("Identified profile {} for {}", rewriter.Profile, path); - if (rewriter.Profile.BakedOutputSubdirectory is not null) - { - var discrim = $"sources/{rewriter.Profile.BakedOutputSubdirectory.Trim('/')}"; - if (!bakery.TryGetValue(discrim, out var bakeSet)) - { - bakeSet = bakery[discrim] = new BakeSet(); - } + context.Rewrite(rewriter); - rewriter.Baked = bakeSet; - baked.Add(path); - } - - syntax.Files[path] = rewriter.Visit(root); - foreach (var (k, v) in rewriter.UsingsToAdd) - { - aggregatedUsings.TryAdd(k, v); - } - rewriter.UsingsToAdd.Clear(); - rewriter.Baked = null; - rewriter.Profile = null; - } - - foreach (var path in baked) - { - syntax.Files.Remove(path); - } - - foreach (var (subdir, bakeSet) in bakery) - { - foreach (var (fqTopLevelType, bakedMember) in bakeSet.Children) - { - var (iden, bakedSyntax) = Bake(bakedMember); - if (iden is null) - { - throw new InvalidOperationException( - "Cannot output an unidentified syntax. Top-level syntax should be type declarations only." - ); - } - - var ns = fqTopLevelType.LastIndexOf('.') is not -1 and var idx - ? fqTopLevelType[..idx] - : null; - syntax.Files[$"{subdir}/{PathForFullyQualified(fqTopLevelType)}"] = - CompilationUnit() - .WithMembers( - ns is null - ? SingletonList(bakedSyntax) - : SingletonList( - FileScopedNamespaceDeclaration( - ModUtils.NamespaceIntoIdentifierName(ns) - ) - .WithMembers(SingletonList(bakedSyntax)) - ) - ) - .WithUsings(ModCSharpSyntaxRewriter.GetUsings(aggregatedUsings, null)); - } - } - - return Task.FromResult(syntax); + return Task.FromResult(context); } - - private static (string? Identifier, MemberDeclarationSyntax Syntax) Bake( - (MemberDeclarationSyntax Syntax, BakeSet? Inner, int Index) member - ) => - member.Syntax switch - { - TypeDeclarationSyntax ty - => ( - ty.Identifier - + ( - ty.TypeParameterList is { Parameters.Count: > 0 and var cnt } - ? $"`{cnt}" - : string.Empty - ), - ty.WithMembers( - List( - ty.Members.Concat( - member - .Inner?.Children.Values.OrderBy(x => x.Index) - .Select(x => Bake(x).Syntax) - ?? Enumerable.Empty() - ) - ) - ) - ), - EnumDeclarationSyntax enumDecl - => ( - enumDecl.Identifier.ToString(), - enumDecl.WithMembers( - SeparatedList( - enumDecl.Members.Concat( - member - .Inner?.Children.Values.OrderBy(x => x.Index) - .Select(x => x.Syntax) - .OfType() - ?? Enumerable.Empty() - ) - ) - ) - ), - DelegateDeclarationSyntax del - => ( - del.Identifier - + ( - del.TypeParameterList is { Parameters.Count: > 0 and var cnt } - ? $"`{cnt}" - : string.Empty - ), - del - ), - var x => (null, x) - }; } diff --git a/sources/SilkTouch/Mods/AddOpaqueStructs.cs b/sources/SilkTouch/Mods/AddOpaqueStructs.cs index 4b6936988a..1170ed6d46 100644 --- a/sources/SilkTouch/Mods/AddOpaqueStructs.cs +++ b/sources/SilkTouch/Mods/AddOpaqueStructs.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -55,10 +55,10 @@ public Task> BeforeScrapeAsync(string key, List } /// - public Task AfterScrapeAsync(string key, GeneratedSyntax syntax) + public Task AfterScrapeAsync(string key, SyntaxContext context) { var cfg = _config.Get(key); - var diags = new List(syntax.Diagnostics); + var diags = new List(context.Diagnostics); foreach (var name in cfg.Names ?? Array.Empty()) { var qualified = name.LastIndexOf('.'); @@ -80,7 +80,7 @@ public Task AfterScrapeAsync(string key, GeneratedSyntax syntax continue; } - syntax.Files.Add( + context.AddFile( $"sources/{name[(qualified + 1)..]}.gen.cs", CompilationUnit() .WithMembers( @@ -103,6 +103,6 @@ public Task AfterScrapeAsync(string key, GeneratedSyntax syntax ); } - return Task.FromResult(syntax with { Diagnostics = diags }); + return Task.FromResult(context); } } diff --git a/sources/SilkTouch/Mods/AddVTables.cs b/sources/SilkTouch/Mods/AddVTables.cs index 2da61d9c2b..627df24322 100644 --- a/sources/SilkTouch/Mods/AddVTables.cs +++ b/sources/SilkTouch/Mods/AddVTables.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -488,6 +489,9 @@ class Rewriter(VTable[] vTables) : ModCSharpSyntaxRewriter private InterfaceDeclarationSyntax? _currentInterface; private VTable[] _vTables = vTables; + public List<(string, SyntaxNode)> NewFiles = []; + + private ClassDeclarationSyntax?[] _currentVTableOutputs = new ClassDeclarationSyntax?[ vTables.Length ]; @@ -1150,10 +1154,75 @@ callConv is not null ) ) ); + + public override void OnFileStarted(string fileName) + { + InterfacePartials.Clear(); + ClassName = null; + } + + public override void OnFileFinished(string fileName) + { + if (InterfacePartials.Count == 0) + { + return; + } + + var ifname = + ClassName is not null + && fileName.Replace(ClassName, $"I{ClassName}") is var ifn + && ifn != fileName + ? ifn + : ModUtils.AddEffectiveSuffix(fileName, "Interfaces"); + var nNamespaces = InterfacePartials.Select(x => x.Namespace).Distinct().Count(); + NewFiles.Add( + ( + ifname, + CompilationUnit() + .WithUsings( + ModCSharpSyntaxRewriter.GetUsings(AggregatedUsings, null) + ) + .WithMembers( + nNamespaces == 1 + ? string.IsNullOrWhiteSpace(InterfacePartials[0].Namespace) + ? List( + InterfacePartials.Select(x => x.Interface) + ) + : SingletonList( + FileScopedNamespaceDeclaration( + ModUtils.NamespaceIntoIdentifierName( + InterfacePartials[0].Namespace + ) + ) + .WithMembers( + List( + InterfacePartials.Select(x => + x.Interface + ) + ) + ) + ) + : List( + InterfacePartials.GroupBy(x => x.Namespace) + .Select(g => + NamespaceDeclaration( + ModUtils.NamespaceIntoIdentifierName(g.Key) + ) + .WithMembers( + List( + g.Select(x => x.Interface) + ) + ) + ) + ) + ) + ) + ); + } } /// - public Task AfterScrapeAsync(string key, GeneratedSyntax syntax) + public Task AfterScrapeAsync(string key, SyntaxContext context) { var cfg = config.Get(key); VTable[]? vTables = null; @@ -1193,83 +1262,26 @@ public Task AfterScrapeAsync(string key, GeneratedSyntax syntax } ); - var newFiles = new List<(string, SyntaxNode)>(rw.FullClassNames.Count); - foreach (var (fname, node) in syntax.Files) - { - if (fname.StartsWith("sources/")) - { - rw.InterfacePartials.Clear(); - rw.ClassName = null; - syntax.Files[fname] = - rw.Visit(node) ?? throw new InvalidOperationException("Visit returned null"); - if (rw.InterfacePartials.Count == 0) - { - continue; - } + rw.NewFiles = new List<(string, SyntaxNode)>(rw.FullClassNames.Count); - var ifname = - rw.ClassName is not null - && fname.Replace(rw.ClassName, $"I{rw.ClassName}") is var ifn - && ifn != fname - ? ifn - : ModUtils.AddEffectiveSuffix(fname, "Interfaces"); - var nNamespaces = rw.InterfacePartials.Select(x => x.Namespace).Distinct().Count(); - newFiles.Add( - ( - ifname, - CompilationUnit() - .WithUsings( - ModCSharpSyntaxRewriter.GetUsings(rw.AggregatedUsings, null) - ) - .WithMembers( - nNamespaces == 1 - ? string.IsNullOrWhiteSpace(rw.InterfacePartials[0].Namespace) - ? List( - rw.InterfacePartials.Select(x => x.Interface) - ) - : SingletonList( - FileScopedNamespaceDeclaration( - ModUtils.NamespaceIntoIdentifierName( - rw.InterfacePartials[0].Namespace - ) - ) - .WithMembers( - List( - rw.InterfacePartials.Select(x => - x.Interface - ) - ) - ) - ) - : List( - rw.InterfacePartials.GroupBy(x => x.Namespace) - .Select(g => - NamespaceDeclaration( - ModUtils.NamespaceIntoIdentifierName(g.Key) - ) - .WithMembers( - List( - g.Select(x => x.Interface) - ) - ) - ) - ) - ) - ) - ); - } - } + context.Rewrite(rw); - foreach (var (fname, node) in newFiles) + foreach (var (fname, node) in rw.NewFiles) { - syntax.Files[fname] = node; + if (node is CompilationUnitSyntax comp) + { + context.AddFile(fname, comp); + } } foreach (var (fname, node) in rw.GetExtraFiles()) { - syntax.Files[fname] = node; + if (node is CompilationUnitSyntax comp) + { + context.AddFile(fname, comp); + } } - return Task.FromResult(syntax); + return Task.FromResult(context); } } diff --git a/sources/SilkTouch/Mods/ChangeNamespace.cs b/sources/SilkTouch/Mods/ChangeNamespace.cs index 93b54bfe6a..c97aba3291 100644 --- a/sources/SilkTouch/Mods/ChangeNamespace.cs +++ b/sources/SilkTouch/Mods/ChangeNamespace.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -140,7 +140,7 @@ public Task AfterScrapeAsync(string key, GeneratedSyntax syntax /// public Task AfterJobAsync(string key) => Task.FromResult(_jobs.Remove(key)); - private class Rewriter : CSharpSyntaxRewriter + private class Rewriter : ContextCSharpSyntaxRewriter { private readonly HashSet _allNamespaces; private readonly IReadOnlyList<(Regex Regex, string Replacement)> _regexes; @@ -172,38 +172,100 @@ CompilationUnitSyntax syntax public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) { - var oldNs = node.Name.ToString(); + var oldNs = CurrentNamespaceContext?.FullNamespace ?? string.Empty; var newNs = ModUtils.GroupedRegexReplace(_regexes, oldNs); - if (oldNs != newNs && _allNamespaces.Contains(oldNs)) + + if (oldNs == newNs) { - _usingsToAdd.Add(oldNs); + return node; } - return base.VisitNamespaceDeclaration(node) switch + + _usingsToAdd.Add(oldNs); + + var newNames = newNs.Split('.'); + + BaseNamespaceDeclarationSyntax[] namespaces = new BaseNamespaceDeclarationSyntax[newNames.Length]; + + namespaces[namespaces.Length - 1] = CurrentNamespaceContext!.ToCompletedNode()!.WithName(IdentifierName(newNames[namespaces.Length - 1])); + + for (int i = namespaces.Length - 2; i >= 0; i--) { - NamespaceDeclarationSyntax syntax - => syntax.WithName(ModUtils.NamespaceIntoIdentifierName(newNs)), - { } ret => ret, - null => null - }; + namespaces[i] = NamespaceDeclaration(IdentifierName(newNames[i])).WithMembers(List(new MemberDeclarationSyntax[] { namespaces[i + 1] })); + } + + int index = 0; + INamespaceContext currentContext = TopNamespaceContext!; + do + { + if (currentContext.TryGetNamespace(newNames[index], out var ns)) + { + if (index == namespaces.Length - 1) + { + ns!.Merge(namespaces[index], this); + break; + } + + currentContext = ns!; + } + else + { + currentContext.AddNamespace(namespaces[index], this); + break; + } + } + while (true); + + return null; } public override SyntaxNode? VisitFileScopedNamespaceDeclaration( FileScopedNamespaceDeclarationSyntax node ) { - var oldNs = node.Name.ToString(); + var oldNs = CurrentNamespaceContext?.FullNamespace ?? string.Empty; var newNs = ModUtils.GroupedRegexReplace(_regexes, oldNs); - if (oldNs != newNs && _allNamespaces.Contains(oldNs)) + + if (oldNs == newNs) { - _usingsToAdd.Add(oldNs); + return node; } - return base.VisitFileScopedNamespaceDeclaration(node) switch + + _usingsToAdd.Add(oldNs); + + var newNames = newNs.Split('.'); + + BaseNamespaceDeclarationSyntax[] namespaces = new BaseNamespaceDeclarationSyntax[newNames.Length]; + + namespaces[namespaces.Length - 1] = CurrentNamespaceContext!.ToCompletedNode()!.WithName(IdentifierName(newNames[namespaces.Length - 1])); + + for (int i = namespaces.Length - 2; i >= 0; i--) { - FileScopedNamespaceDeclarationSyntax syntax - => syntax.WithName(ModUtils.NamespaceIntoIdentifierName(newNs)), - { } ret => ret, - null => null - }; + namespaces[i] = NamespaceDeclaration(IdentifierName(newNames[i])).WithMembers(List(new MemberDeclarationSyntax[] { namespaces[i + 1] })); + } + + int index = 0; + INamespaceContext currentContext = TopNamespaceContext!; + do + { + if (currentContext.TryGetNamespace(newNames[index], out var ns)) + { + if (index == namespaces.Length - 1) + { + ns!.Merge(namespaces[index], this); + break; + } + + currentContext = ns!; + } + else + { + currentContext.AddNamespace(namespaces[index], this); + break; + } + } + while (true); + + return null; } } } diff --git a/sources/SilkTouch/Mods/Common/Mod.cs b/sources/SilkTouch/Mods/Common/Mod.cs index 518d6cdc24..42270b5e10 100644 --- a/sources/SilkTouch/Mods/Common/Mod.cs +++ b/sources/SilkTouch/Mods/Common/Mod.cs @@ -55,7 +55,7 @@ public virtual Task> BeforeScrapeAsync(string key, List - public virtual Task AfterScrapeAsync(string key, SyntaxContext context) => + public virtual Task AfterScrapeAsync(string key, SyntaxContext context) => Task.FromResult(context); /// diff --git a/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs b/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs index f5a6de60ae..25812af84e 100644 --- a/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs +++ b/sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs @@ -145,4 +145,7 @@ protected bool AddUsing(string str) => /// The discriminator string. protected static string Discrim(UsingDirectiveSyntax use) => new(use.WithoutTrivia().ToString().Where(char.IsAsciiLetterOrDigit).ToArray()); + + /// + public override bool ShouldSkipFile(string fileName) => fileName.StartsWith("sources/"); }