From f204f4e367c2515e2c5dc5e86db612a548801dc6 Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Sun, 4 Apr 2021 09:00:43 +1000 Subject: [PATCH] Use a Source Generator for ReflectionCache classes --- .../BizHawk.SrcGen.ReflectionCache.csproj | 10 ++ .../ReflectionCacheGenerator.cs | 97 +++++++++++++++++++ src/BizHawk.Client.Common/ReflectionCache.cs | 14 --- src/BizHawk.Client.EmuHawk/ReflectionCache.cs | 20 ---- .../ReflectionCache.cs | 14 --- .../ReflectionCache.cs | 17 ---- .../BizHawk.WinForms.Controls.csproj | 1 + src/MainSlnCommon.props | 1 + 8 files changed, 109 insertions(+), 65 deletions(-) create mode 100644 ExternalProjects/BizHawk.SrcGen.ReflectionCache/BizHawk.SrcGen.ReflectionCache.csproj create mode 100644 ExternalProjects/BizHawk.SrcGen.ReflectionCache/ReflectionCacheGenerator.cs delete mode 100644 src/BizHawk.Client.Common/ReflectionCache.cs delete mode 100644 src/BizHawk.Client.EmuHawk/ReflectionCache.cs delete mode 100644 src/BizHawk.Emulation.Common/ReflectionCache.cs delete mode 100644 src/BizHawk.Emulation.Cores/ReflectionCache.cs diff --git a/ExternalProjects/BizHawk.SrcGen.ReflectionCache/BizHawk.SrcGen.ReflectionCache.csproj b/ExternalProjects/BizHawk.SrcGen.ReflectionCache/BizHawk.SrcGen.ReflectionCache.csproj new file mode 100644 index 0000000000..570e04b100 --- /dev/null +++ b/ExternalProjects/BizHawk.SrcGen.ReflectionCache/BizHawk.SrcGen.ReflectionCache.csproj @@ -0,0 +1,10 @@ + + + netstandard2.0 + + + + + + + diff --git a/ExternalProjects/BizHawk.SrcGen.ReflectionCache/ReflectionCacheGenerator.cs b/ExternalProjects/BizHawk.SrcGen.ReflectionCache/ReflectionCacheGenerator.cs new file mode 100644 index 0000000000..3eef03ae05 --- /dev/null +++ b/ExternalProjects/BizHawk.SrcGen.ReflectionCache/ReflectionCacheGenerator.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace BizHawk.SrcGen.ReflectionCache +{ + [Generator] + public sealed class ReflectionCacheGenerator : ISourceGenerator + { + private sealed class ReflectionCacheGenSyntaxReceiver : ISyntaxReceiver + { + /// + /// I may have just added RNG to the build process... + /// Increase this sample size to decrease chance of random failure. + /// Alternatively, if you can come up with a better way of getting the project name in (I tried like 5 different things), do that instead. + /// --yoshi + /// + private const int SAMPLE_SIZE = 20; + + private string? _namespace; + + private readonly List _namespaces = new(); + + public string Namespace => _namespace ??= CalcNamespace(); + + private string CalcNamespace() + { + // black magic wizardry to find common prefix https://stackoverflow.com/a/35081977 + var ns = new string(_namespaces.First() + .Substring(0, _namespaces.Min(s => s.Length)) + .TakeWhile((c, i) => _namespaces.All(s => s[i] == c)) + .ToArray()); + return ns[ns.Length - 1] == '.' ? ns.Substring(0, ns.Length - 1) : ns; // trim trailing '.' (can't use BizHawk.Common from Source Generators) + } + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + static string Ser(NameSyntax nameSyn) => nameSyn switch + { + SimpleNameSyntax simple => simple.Identifier.ValueText, + QualifiedNameSyntax qual => $"{Ser(qual.Left)}.{Ser(qual.Right)}", + _ => throw new Exception() + }; + if (_namespace != null || syntaxNode is not NamespaceDeclarationSyntax syn) return; + var newNS = Ser(syn.Name); + if (!newNS.StartsWith("BizHawk.")) return; + _namespaces.Add(newNS); + if (_namespaces.Count == SAMPLE_SIZE) _namespace = CalcNamespace(); + } + } + + public void Initialize(GeneratorInitializationContext context) + => context.RegisterForSyntaxNotifications(() => new ReflectionCacheGenSyntaxReceiver()); + + public void Execute(GeneratorExecutionContext context) + { + if (context.SyntaxReceiver is not ReflectionCacheGenSyntaxReceiver receiver) return; + var nSpace = receiver.Namespace; + if (nSpace == null) return; + var extraImports = nSpace == "BizHawk.Common" ? string.Empty : "\nusing BizHawk.Common;"; + var src = $@"#nullable enable + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +{extraImports} + +namespace {nSpace} +{{ + public static class ReflectionCache + {{ + private static readonly Assembly Asm = typeof({nSpace}.ReflectionCache).Assembly; + + private static readonly Lazy _types = new Lazy(() => Asm.GetTypesWithoutLoadErrors().ToArray()); + + public static Type[] Types => _types.Value; + + /// not found + public static Stream EmbeddedResourceStream(string embedPath) + {{ + var fullPath = $""{nSpace}.{{embedPath}}""; + return Asm.GetManifestResourceStream(fullPath) ?? throw new ArgumentException(""resource at {{fullPath}} not found"", nameof(embedPath)); + }} + }} +}} +"; + context.AddSource("ReflectionCache.cs", SourceText.From(src, Encoding.UTF8)); + } + } +} diff --git a/src/BizHawk.Client.Common/ReflectionCache.cs b/src/BizHawk.Client.Common/ReflectionCache.cs deleted file mode 100644 index d9c1d21370..0000000000 --- a/src/BizHawk.Client.Common/ReflectionCache.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Reflection; - -namespace BizHawk.Client.Common -{ - public static class ReflectionCache - { - private static readonly Lazy _types = new Lazy(() => Asm.GetTypes()); - - private static readonly Assembly Asm = typeof(ReflectionCache).Assembly; - - public static Type[] Types => _types.Value; - } -} diff --git a/src/BizHawk.Client.EmuHawk/ReflectionCache.cs b/src/BizHawk.Client.EmuHawk/ReflectionCache.cs deleted file mode 100644 index 5d0da14365..0000000000 --- a/src/BizHawk.Client.EmuHawk/ReflectionCache.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; - -using BizHawk.Common; - -namespace BizHawk.Client.EmuHawk -{ - public static class ReflectionCache - { - private static readonly Lazy _types = new Lazy(() => Asm.GetTypesWithoutLoadErrors().ToArray()); - - private static readonly Assembly Asm = typeof(ReflectionCache).Assembly; - - public static Type[] Types => _types.Value; - - public static Stream EmbeddedResourceStream(string embedPath) => Asm.GetManifestResourceStream($"BizHawk.Client.EmuHawk.{embedPath}"); - } -} diff --git a/src/BizHawk.Emulation.Common/ReflectionCache.cs b/src/BizHawk.Emulation.Common/ReflectionCache.cs deleted file mode 100644 index f10d87347c..0000000000 --- a/src/BizHawk.Emulation.Common/ReflectionCache.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Reflection; - -namespace BizHawk.Emulation.Common -{ - public static class ReflectionCache - { - private static readonly Lazy _types = new Lazy(() => Asm.GetTypes()); - - private static readonly Assembly Asm = typeof(ReflectionCache).Assembly; - - public static Type[] Types => _types.Value; - } -} diff --git a/src/BizHawk.Emulation.Cores/ReflectionCache.cs b/src/BizHawk.Emulation.Cores/ReflectionCache.cs deleted file mode 100644 index 669d8aa775..0000000000 --- a/src/BizHawk.Emulation.Cores/ReflectionCache.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace BizHawk.Emulation.Cores -{ - public static class ReflectionCache - { - private static readonly Lazy _types = new Lazy(() => Asm.GetTypes()); - - private static readonly Assembly Asm = typeof(ReflectionCache).Assembly; - - public static Type[] Types => _types.Value; - - public static Stream EmbeddedResourceStream(string embedPath) => Asm.GetManifestResourceStream($"BizHawk.Emulation.Cores.{embedPath}"); - } -} diff --git a/src/BizHawk.WinForms.Controls/BizHawk.WinForms.Controls.csproj b/src/BizHawk.WinForms.Controls/BizHawk.WinForms.Controls.csproj index 7d82a7c78f..db07bf0013 100644 --- a/src/BizHawk.WinForms.Controls/BizHawk.WinForms.Controls.csproj +++ b/src/BizHawk.WinForms.Controls/BizHawk.WinForms.Controls.csproj @@ -5,5 +5,6 @@ + diff --git a/src/MainSlnCommon.props b/src/MainSlnCommon.props index af47f01b70..638a1b414b 100644 --- a/src/MainSlnCommon.props +++ b/src/MainSlnCommon.props @@ -10,5 +10,6 @@ +