Use a Source Generator for ReflectionCache classes
This commit is contained in:
parent
86dd250a73
commit
f204f4e367
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="../../Common.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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
|
||||
{
|
||||
/// <remarks>
|
||||
/// 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 <see cref="ISourceGenerator.Execute"/> (I tried like 5 different things), do that instead.
|
||||
/// --yoshi
|
||||
/// </remarks>
|
||||
private const int SAMPLE_SIZE = 20;
|
||||
|
||||
private string? _namespace;
|
||||
|
||||
private readonly List<string> _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<Type[]> _types = new Lazy<Type[]>(() => Asm.GetTypesWithoutLoadErrors().ToArray());
|
||||
|
||||
public static Type[] Types => _types.Value;
|
||||
|
||||
/// <exception cref=""ArgumentException"">not found</exception>
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public static class ReflectionCache
|
||||
{
|
||||
private static readonly Lazy<Type[]> _types = new Lazy<Type[]>(() => Asm.GetTypes());
|
||||
|
||||
private static readonly Assembly Asm = typeof(ReflectionCache).Assembly;
|
||||
|
||||
public static Type[] Types => _types.Value;
|
||||
}
|
||||
}
|
|
@ -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<Type[]> _types = new Lazy<Type[]>(() => 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}");
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public static class ReflectionCache
|
||||
{
|
||||
private static readonly Lazy<Type[]> _types = new Lazy<Type[]>(() => Asm.GetTypes());
|
||||
|
||||
private static readonly Assembly Asm = typeof(ReflectionCache).Assembly;
|
||||
|
||||
public static Type[] Types => _types.Value;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BizHawk.Emulation.Cores
|
||||
{
|
||||
public static class ReflectionCache
|
||||
{
|
||||
private static readonly Lazy<Type[]> _types = new Lazy<Type[]>(() => 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}");
|
||||
}
|
||||
}
|
|
@ -5,5 +5,6 @@
|
|||
<Import Project="../MainSlnCommon.props" />
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<ProjectReference Include="$(ProjectDir)../BizHawk.Common/BizHawk.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -10,5 +10,6 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="$(ProjectDir)../../.stylecop.json" />
|
||||
<ProjectReference Include="$(ProjectDir)../../ExternalProjects/BizHawk.SrcGen.ReflectionCache/BizHawk.SrcGen.ReflectionCache.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue