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
|
||||
{
|
||||
private sealed class EditorAssemblyLoadContextWrapper
|
||||
internal sealed class EditorAssemblyLoadContextWrapper
|
||||
{
|
||||
private EditorAssemblyLoadContext assemblyLoadContext;
|
||||
private readonly WeakReference weakReference;
|
||||
@ -44,6 +44,16 @@ public static (Assembly, EditorAssemblyLoadContextWrapper) CreateAndLoad(Assembl
|
||||
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)]
|
||||
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);
|
||||
|
||||
@ -61,7 +71,12 @@ private static (Assembly, EditorAssemblyLoadContextWrapper) Load(string assembly
|
||||
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)
|
||||
{
|
||||
|
@ -11,6 +11,8 @@ internal static class EditorProject
|
||||
internal static string ProjectContentDirectory { 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)
|
||||
{
|
||||
Close();
|
||||
@ -80,7 +82,11 @@ internal static void Compile()
|
||||
return;
|
||||
}
|
||||
|
||||
UnloadAssemblies();
|
||||
|
||||
Compiler.Compiler.Compile(ProjectFilePath, "Debug");
|
||||
|
||||
LoadAssemblies();
|
||||
}
|
||||
|
||||
internal static void GenerateSolution()
|
||||
@ -132,4 +138,36 @@ private static void SetupDefaultFolders()
|
||||
}
|
||||
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();
|
||||
|
||||
OnInitialize?.Invoke();
|
||||
Nerfed.Runtime.Generator.Hook.InvokeHooks();
|
||||
|
||||
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>
|
||||
<Configurations>Debug;Test;Release</Configurations>
|
||||
<Platforms>x64</Platforms>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -40,4 +41,10 @@
|
||||
<Compile Include="Libraries\ImGui.NET\src\ImGui.NET\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nerfed.Runtime.Generator\Nerfed.Runtime.Generator.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Editor", "Nerfed.Edi
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Compiler", "Nerfed.Compiler\Nerfed.Compiler.csproj", "{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nerfed.Runtime.Generator", "Nerfed.Runtime.Generator\Nerfed.Runtime.Generator.csproj", "{8743FDEF-4FF6-48F9-9F64-7BDEC543C105}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Test|x64.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
Loading…
Reference in New Issue
Block a user