From f2a47941058e0582c44014138911120ccce7d410 Mon Sep 17 00:00:00 2001
From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com>
Date: Wed, 25 Oct 2023 22:50:17 -0700
Subject: [PATCH] Use source generation for SettingsUtil.SetDefaultValues
---
.../AnalyzersCommon/RoslynUtils.cs | 4 +-
.../BizHawk.SrcGen.SettingsUtil/.gitignore | 2 +
.../BizHawk.SrcGen.SettingsUtil.csproj | 6 +
.../DefaultSetterGenerator.cs | 117 ++++++++++++++++++
.../build_debug.sh | 1 +
.../build_release.sh | 1 +
References/BizHawk.SrcGen.SettingsUtil.dll | Bin 0 -> 12800 bytes
src/BizHawk.Common/BizHawk.Common.csproj | 1 -
src/BizHawk.Common/SettingsUtil.cs | 111 -----------------
.../Interfaces/Services/ISettable.cs | 11 ++
.../Arcades/MAME/MAME.ISettable.cs | 1 +
.../BizHawk.Emulation.Cores.csproj | 1 +
.../AmstradCPC/AmstradCPC.ISettable.cs | 2 +
.../Computers/AppleII/AppleII.ISettable.cs | 2 +
.../Computers/Commodore64/C64.ISettable.cs | 8 +-
.../Computers/MSX/MSX.ISettable.cs | 2 +
.../SinclairSpectrum/ZXSpectrum.ISettable.cs | 2 +
.../Computers/TIC80/TIC80.ISettable.cs | 2 +
.../Atari/2600/Atari2600.ISettable.cs | 2 +
.../Atari/A7800Hawk/A7800Hawk.ISettable.cs | 57 ++-------
.../Consoles/Atari/A7800Hawk/A7800Hawk.cs | 5 +-
.../Atari/jaguar/VirtualJaguar.ISettable.cs | 2 +
.../Fairchild/ChannelF/ChannelF.ISettable.cs | 2 +
.../GCE/Vectrex/VectrexHawk.ISettable.cs | 1 +
.../Magnavox/Odyssey2/O2Hawk.ISettable.cs | 2 +
.../Consoles/Nintendo/3DS/Citra.ISettable.cs | 2 +
.../Nintendo/Ares64/Ares64.ISettable.cs | 2 +
.../Nintendo/GBA/MGBAHawk.ISettable.cs | 2 +
.../Nintendo/GBHawk/GBHawk.ISettable.cs | 2 +
.../GBHawkLink/GBHawkLink.ISettable.cs | 2 +
.../GBHawkLink3x/GBHawkLink3x.ISettable.cs | 2 +
.../GBHawkLink4x/GBHawkLink4x.ISettable.cs | 2 +
.../Nintendo/Gameboy/Gambatte.ISettable.cs | 1 +
.../Nintendo/NDS/MelonDS.ISettable.cs | 2 +
.../Nintendo/QuickNES/QuickNES.ISettable.cs | 2 +
.../Consoles/Nintendo/SNES9X/Snes9x.cs | 2 +
.../Nintendo/SameBoy/SameBoy.ISettable.cs | 2 +
.../Sega/GGHawkLink/GGHawkLink.ISettable.cs | 36 +++---
.../Consoles/Sega/PicoDrive/PicoDrive.cs | 1 +
.../Consoles/Sega/gpgx64/GPGX.ISettable.cs | 2 +
.../Consoles/Sony/PSX/Octoshock.cs | 1 +
.../WonderSwan/WonderSwan.ISettable.cs | 7 +-
42 files changed, 229 insertions(+), 186 deletions(-)
create mode 100644 ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore
create mode 100644 ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj
create mode 100644 ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs
create mode 100644 ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh
create mode 100644 ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh
create mode 100644 References/BizHawk.SrcGen.SettingsUtil.dll
delete mode 100644 src/BizHawk.Common/SettingsUtil.cs
diff --git a/ExternalProjects/AnalyzersCommon/RoslynUtils.cs b/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
index cc8b60d094..7d3e3fea55 100644
--- a/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
+++ b/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
@@ -1,9 +1,9 @@
-namespace BizHawk.Analyzers;
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace BizHawk.Analyzers;
+
public static class RoslynUtils
{
public static SyntaxNode? EnclosingTypeDeclarationSyntax(this CSharpSyntaxNode node)
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore
new file mode 100644
index 0000000000..4c7473dedd
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/obj
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj
new file mode 100644
index 0000000000..46dbd2596a
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj
@@ -0,0 +1,6 @@
+
+
+ netstandard2.0
+
+
+
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs
new file mode 100644
index 0000000000..77151014e5
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace BizHawk.SrcGen.SettingsUtil;
+
+[Generator]
+public class DefaultSetterGenerator : ISourceGenerator
+{
+ public class SyntaxReceiver : ISyntaxContextReceiver
+ {
+ public readonly List<(ClassDeclarationSyntax, SemanticModel)> ClassDeclarations = new();
+
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
+ {
+ if (context.Node is ClassDeclarationSyntax cds)
+ {
+ ClassDeclarations.Add((cds, context.SemanticModel));
+ }
+ }
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ => context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+
+ private static void CreateDefaultSetter(StringBuilder source, INamespaceOrTypeSymbol symbol)
+ {
+ var props = symbol
+ .GetMembers()
+ .Where(m => m.Kind == SymbolKind.Property)
+ .ToImmutableArray();
+
+ source.Append($@"
+ public static void SetDefaultValues({symbol} settings)
+ {{");
+
+ foreach (var prop in props)
+ {
+ var defaultValueAttribute = prop
+ .GetAttributes()
+ .FirstOrDefault(
+ a => a.AttributeClass?.Name == "DefaultValueAttribute");
+
+ var ctorArgs = defaultValueAttribute?.ConstructorArguments;
+ if (!ctorArgs.HasValue)
+ {
+ continue;
+ }
+
+ switch (ctorArgs.Value.Length)
+ {
+ case 1:
+ // this single arg is just the value assigned to the default value
+ var arg = ctorArgs.Value[0];
+ // a bit lame, but it'll work
+ // TODO: do we even want to handle arrays? do we even have any arrays in default values???
+ var converionStr = arg.Kind == TypedConstantKind.Array
+ ? $"new {arg.Type} " // new T[]
+ : ""; // do we need a cast (i.e. (T)) here? probably not?
+ source.Append($@"
+ settings.{prop.Name} = {converionStr}{arg.ToCSharpString()};");
+ break;
+ case 2:
+ // first arg is the type, the second arg is a string which converts it
+ source.Append($@"
+ settings.{prop.Name} = ({ctorArgs.Value[0].Value})System.ComponentModel.TypeDescriptor
+ .GetConverter({ctorArgs.Value[0].ToCSharpString()})
+ .ConvertFromInvariantString({ctorArgs.Value[1].ToCSharpString()});");
+ break;
+ }
+ }
+
+ source.Append(@"
+ }
+");
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver)
+ {
+ return;
+ }
+
+ // Generated source code
+ var source = new StringBuilder(@"
+namespace BizHawk.Common
+{
+ public static partial class SettingsUtil
+ {");
+
+ foreach (var (cds, semanticModel) in syntaxReceiver.ClassDeclarations)
+ {
+ if (cds.AttributeLists.SelectMany(e => e.Attributes)
+ .Any(e => e.Name.NormalizeWhitespace().ToFullString() == "CoreSettings"))
+ {
+ var symbol = semanticModel.GetDeclaredSymbol(cds);
+ if (symbol is not null) // probably never happens?
+ {
+ CreateDefaultSetter(source, symbol);
+ }
+ }
+ }
+
+ source.Append(@"
+ }
+}");
+
+ // Add the source code to the compilation
+ context.AddSource("DefaultSetters.g.cs", source.ToString());
+ }
+}
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh
new file mode 100644
index 0000000000..c2127aded1
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh
@@ -0,0 +1 @@
+../.build_debug.sh
\ No newline at end of file
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh
new file mode 100644
index 0000000000..801b8e5998
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh
@@ -0,0 +1 @@
+../.build_release.sh
\ No newline at end of file
diff --git a/References/BizHawk.SrcGen.SettingsUtil.dll b/References/BizHawk.SrcGen.SettingsUtil.dll
new file mode 100644
index 0000000000000000000000000000000000000000..94a2b3fafaed891a13fb089a42e86478ea2d4c91
GIT binary patch
literal 12800
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
-
diff --git a/src/BizHawk.Common/SettingsUtil.cs b/src/BizHawk.Common/SettingsUtil.cs
deleted file mode 100644
index 25176ac761..0000000000
--- a/src/BizHawk.Common/SettingsUtil.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Collections.Concurrent;
-using System.ComponentModel;
-
-using BizHawk.Common.CollectionExtensions;
-
-namespace BizHawk.Common
-{
- public static class SettingsUtil
- {
- private sealed class DefaultValueSetter
- {
- public readonly Action