Test with generated hooks
This commit is contained in:
parent
bd76fc1b25
commit
09089c35b9
@ -10,7 +10,7 @@ namespace Nerfed.Editor.Project;
|
|||||||
|
|
||||||
internal static class EditorAssemblyLoader
|
internal static class EditorAssemblyLoader
|
||||||
{
|
{
|
||||||
private sealed class EditorAssemblyLoadContextWrapper
|
internal sealed class EditorAssemblyLoadContextWrapper
|
||||||
{
|
{
|
||||||
private EditorAssemblyLoadContext assemblyLoadContext;
|
private EditorAssemblyLoadContext assemblyLoadContext;
|
||||||
private readonly WeakReference weakReference;
|
private readonly WeakReference weakReference;
|
||||||
@ -44,6 +44,16 @@ public static (Assembly, EditorAssemblyLoadContextWrapper) CreateAndLoad(Assembl
|
|||||||
return (assembly, wrapper);
|
return (assembly, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static (Assembly, EditorAssemblyLoadContextWrapper) CreateAndLoad(string assemblyPath)
|
||||||
|
{
|
||||||
|
EditorAssemblyLoadContext context = new EditorAssemblyLoadContext();
|
||||||
|
WeakReference reference = new WeakReference(context, trackResurrection: true);
|
||||||
|
EditorAssemblyLoadContextWrapper wrapper = new EditorAssemblyLoadContextWrapper(context, reference);
|
||||||
|
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
|
||||||
|
return (assembly, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
internal void Unload()
|
internal void Unload()
|
||||||
{
|
{
|
||||||
@ -52,7 +62,7 @@ internal void Unload()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (Assembly, EditorAssemblyLoadContextWrapper) Load(string assemblyFilePath)
|
internal static (Assembly, EditorAssemblyLoadContextWrapper) Load(string assemblyFilePath)
|
||||||
{
|
{
|
||||||
string assemblyFileName = Path.GetFileNameWithoutExtension(assemblyFilePath);
|
string assemblyFileName = Path.GetFileNameWithoutExtension(assemblyFilePath);
|
||||||
|
|
||||||
@ -61,7 +71,12 @@ private static (Assembly, EditorAssemblyLoadContextWrapper) Load(string assembly
|
|||||||
return EditorAssemblyLoadContextWrapper.CreateAndLoad(assemblyName);
|
return EditorAssemblyLoadContextWrapper.CreateAndLoad(assemblyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool Unload(EditorAssemblyLoadContextWrapper assemblyLoadContextWrapper)
|
internal static (Assembly, EditorAssemblyLoadContextWrapper) LoadFromPath(string assemblyFilePath)
|
||||||
|
{
|
||||||
|
return EditorAssemblyLoadContextWrapper.CreateAndLoad(assemblyFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool Unload(EditorAssemblyLoadContextWrapper assemblyLoadContextWrapper)
|
||||||
{
|
{
|
||||||
if (assemblyLoadContextWrapper == null)
|
if (assemblyLoadContextWrapper == null)
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,8 @@ internal static class EditorProject
|
|||||||
internal static string ProjectContentDirectory { get; private set; } = string.Empty;
|
internal static string ProjectContentDirectory { get; private set; } = string.Empty;
|
||||||
internal static string ProjectTempDirectory { get; private set; } = string.Empty;
|
internal static string ProjectTempDirectory { get; private set; } = string.Empty;
|
||||||
|
|
||||||
|
private static readonly List<(string, EditorAssemblyLoader.EditorAssemblyLoadContextWrapper)> editorAssemblyLoadContextWrappers = [];
|
||||||
|
|
||||||
internal static bool Create(string projectFilePath, string projectName)
|
internal static bool Create(string projectFilePath, string projectName)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
@ -80,7 +82,11 @@ internal static void Compile()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnloadAssemblies();
|
||||||
|
|
||||||
Compiler.Compiler.Compile(ProjectFilePath, "Debug");
|
Compiler.Compiler.Compile(ProjectFilePath, "Debug");
|
||||||
|
|
||||||
|
LoadAssemblies();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void GenerateSolution()
|
internal static void GenerateSolution()
|
||||||
@ -132,4 +138,36 @@ private static void SetupDefaultFolders()
|
|||||||
}
|
}
|
||||||
ProjectTempDirectory = tempDirectory;
|
ProjectTempDirectory = tempDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LoadAssemblies()
|
||||||
|
{
|
||||||
|
string[] assemblies = Directory.GetFiles(Path.Combine(ProjectDirectory, "bin"), "*.dll", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
foreach (string assembly in assemblies)
|
||||||
|
{
|
||||||
|
(System.Reflection.Assembly, EditorAssemblyLoader.EditorAssemblyLoadContextWrapper) a = EditorAssemblyLoader.LoadFromPath(assembly);
|
||||||
|
string name = a.Item1.GetName().Name;
|
||||||
|
editorAssemblyLoadContextWrappers.Add((name, a.Item2));
|
||||||
|
Log.Info($"loaded {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Nerfed.Runtime.Generator.Hook.InvokeHooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UnloadAssemblies()
|
||||||
|
{
|
||||||
|
for (int i = editorAssemblyLoadContextWrappers.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
(string, EditorAssemblyLoader.EditorAssemblyLoadContextWrapper) a = editorAssemblyLoadContextWrappers[i];
|
||||||
|
if (EditorAssemblyLoader.Unload(a.Item2))
|
||||||
|
{
|
||||||
|
Log.Info($"Unloaded {a.Item1}");
|
||||||
|
editorAssemblyLoadContextWrappers.RemoveAt(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error($"Could not unload {a.Item1}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
91
Nerfed.Runtime.Generator/HookSourceGenerator.cs
Normal file
91
Nerfed.Runtime.Generator/HookSourceGenerator.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Nerfed.Runtime.Generator
|
||||||
|
{
|
||||||
|
[Generator]
|
||||||
|
public class HookSourceGenerator : ISourceGenerator
|
||||||
|
{
|
||||||
|
public void Execute(GeneratorExecutionContext context)
|
||||||
|
{
|
||||||
|
// Ensure the syntax receiver is not null and is of the expected type
|
||||||
|
if (context.SyntaxReceiver is not HookSyntaxReceiver syntaxReceiver)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if we have collected any hook methods
|
||||||
|
List<MethodDeclarationSyntax> hookMethods = syntaxReceiver.HookMethods;
|
||||||
|
if (hookMethods == null || !hookMethods.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
StringBuilder codeBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
codeBuilder.AppendLine("using System;");
|
||||||
|
codeBuilder.AppendLine("");
|
||||||
|
codeBuilder.AppendLine("namespace Nerfed.Runtime.Generator;");
|
||||||
|
codeBuilder.AppendLine("");
|
||||||
|
codeBuilder.AppendLine($"// Generated by {typeof(HookSourceGenerator)}");
|
||||||
|
codeBuilder.AppendLine("public static class Hook");
|
||||||
|
codeBuilder.AppendLine("{");
|
||||||
|
codeBuilder.AppendLine(" public static void InvokeHooks()");
|
||||||
|
codeBuilder.AppendLine(" {");
|
||||||
|
|
||||||
|
foreach (MethodDeclarationSyntax method in hookMethods)
|
||||||
|
{
|
||||||
|
SemanticModel model = context.Compilation.GetSemanticModel(method.SyntaxTree);
|
||||||
|
|
||||||
|
if (model.GetDeclaredSymbol(method) is not IMethodSymbol methodSymbol)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodSymbol.DeclaredAccessibility != Accessibility.Public || !methodSymbol.IsStatic)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBuilder.AppendLine($" {methodSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}.{methodSymbol.Name}();");
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBuilder.AppendLine(" }");
|
||||||
|
codeBuilder.AppendLine("}");
|
||||||
|
|
||||||
|
// Add the generated code to the compilation
|
||||||
|
context.AddSource("Hook.g.cs", codeBuilder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Initialize(GeneratorInitializationContext context)
|
||||||
|
{
|
||||||
|
context.RegisterForSyntaxNotifications(() => new HookSyntaxReceiver());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HookSyntaxReceiver : ISyntaxReceiver
|
||||||
|
{
|
||||||
|
public List<MethodDeclarationSyntax> HookMethods { get; } = new List<MethodDeclarationSyntax>();
|
||||||
|
|
||||||
|
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||||
|
{
|
||||||
|
// Check if the node is a method declaration
|
||||||
|
if (syntaxNode is MethodDeclarationSyntax methodDeclaration)
|
||||||
|
{
|
||||||
|
// Ensure the method declaration has attribute lists
|
||||||
|
if (methodDeclaration.AttributeLists.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if the method has the Hook attribute
|
||||||
|
bool hasHookAttribute = methodDeclaration.AttributeLists
|
||||||
|
.SelectMany(attrList => attrList.Attributes)
|
||||||
|
.Any(attr => attr.Name.ToString() == "Hook");
|
||||||
|
|
||||||
|
if (hasHookAttribute)
|
||||||
|
{
|
||||||
|
HookMethods.Add(methodDeclaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Nerfed.Runtime.Generator/Nerfed.Runtime.Generator.csproj
Normal file
33
Nerfed.Runtime.Generator/Nerfed.Runtime.Generator.csproj
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
|
<Configurations>Debug;Test;Release</Configurations>
|
||||||
|
<Platforms>x64</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||||
|
<DefineConstants>TRACE;LOG_INFO;PROFILING</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Test|x64' ">
|
||||||
|
<DefineConstants>TRACE;LOG_ERROR;PROFILING</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||||
|
<DefineConstants>TRACE;LOG_ERROR</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -72,6 +72,7 @@ public static void Run(string[] args)
|
|||||||
AudioDevice = new AudioDevice();
|
AudioDevice = new AudioDevice();
|
||||||
|
|
||||||
OnInitialize?.Invoke();
|
OnInitialize?.Invoke();
|
||||||
|
Nerfed.Runtime.Generator.Hook.InvokeHooks();
|
||||||
|
|
||||||
while (!quit)
|
while (!quit)
|
||||||
{
|
{
|
||||||
|
7
Nerfed.Runtime/Hook/HookAttribute.cs
Normal file
7
Nerfed.Runtime/Hook/HookAttribute.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Nerfed.Runtime.Hook
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||||
|
public class HookAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
11
Nerfed.Runtime/Hook/HookTest.cs
Normal file
11
Nerfed.Runtime/Hook/HookTest.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Nerfed.Runtime.Hook
|
||||||
|
{
|
||||||
|
public static class HookTest
|
||||||
|
{
|
||||||
|
[Hook]
|
||||||
|
public static void Test()
|
||||||
|
{
|
||||||
|
Log.Info("Hook!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<Configurations>Debug;Test;Release</Configurations>
|
<Configurations>Debug;Test;Release</Configurations>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
|
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@ -40,4 +41,10 @@
|
|||||||
<Compile Include="Libraries\ImGui.NET\src\ImGui.NET\**\*.cs" />
|
<Compile Include="Libraries\ImGui.NET\src\ImGui.NET\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Nerfed.Runtime.Generator\Nerfed.Runtime.Generator.csproj"
|
||||||
|
OutputItemType="Analyzer"
|
||||||
|
ReferenceOutputAssembly="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Editor", "Nerfed.Edi
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Compiler", "Nerfed.Compiler\Nerfed.Compiler.csproj", "{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Compiler", "Nerfed.Compiler\Nerfed.Compiler.csproj", "{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nerfed.Runtime.Generator", "Nerfed.Runtime.Generator\Nerfed.Runtime.Generator.csproj", "{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
@ -45,6 +47,12 @@ Global
|
|||||||
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Release|x64.Build.0 = Release|x64
|
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Release|x64.Build.0 = Release|x64
|
||||||
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.ActiveCfg = Test|x64
|
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.ActiveCfg = Test|x64
|
||||||
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.Build.0 = Test|x64
|
{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.Build.0 = Test|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Release|x64.Build.0 = Release|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Test|x64.ActiveCfg = Test|x64
|
||||||
|
{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}.Test|x64.Build.0 = Test|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user