Plugins: Implement ReentrantAttribute

This commit is contained in:
Katy Coe
2020-12-26 23:32:21 +01:00
parent 14e80c3022
commit 863aa5ed7d
4 changed files with 36 additions and 7 deletions

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Il2CppInspector.PluginAPI
{
/// <summary>
/// Setting this attribute on an interface method will allow the method to be called again
/// while it is already running, ie. it enables recursion. Disabled by default.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class ReentrantAttribute : Attribute { }
}

View File

@@ -15,6 +15,7 @@ namespace Il2CppInspector
{ {
// Internal helpers to call the same hook on every plugin // Internal helpers to call the same hook on every plugin
// Does not include hooks that should be called individually, eg. OptionsChanged // Does not include hooks that should be called individually, eg. OptionsChanged
// NOTE: The method names must be identical to the interface method names for stack tracing to work
internal static class PluginHooks internal static class PluginHooks
{ {
public static PluginLoadPipelineStartingEventInfo LoadPipelineStarting() public static PluginLoadPipelineStartingEventInfo LoadPipelineStarting()

View File

@@ -11,7 +11,9 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
using Il2CppInspector.PluginAPI;
// This is the ONLY line to update when the API version changes // This is the ONLY line to update when the API version changes
using Il2CppInspector.PluginAPI.V100; using Il2CppInspector.PluginAPI.V100;
@@ -35,6 +37,9 @@ namespace Il2CppInspector
get => Plugin.Options.Single(o => o.Name == s).Value; get => Plugin.Options.Single(o => o.Name == s).Value;
set => Plugin.Options.Single(o => o.Name == s).Value = value; set => Plugin.Options.Single(o => o.Name == s).Value = value;
} }
// The current stack trace of the plugin
internal Stack<string> StackTrace = new Stack<string>();
} }
// Event arguments for error handler // Event arguments for error handler
@@ -46,7 +51,7 @@ namespace Il2CppInspector
// The exception thrown // The exception thrown
public Exception Exception { get; set; } public Exception Exception { get; set; }
// The name of the operation that was being performed // The name of the method that was being executed
public string Operation { get; set; } public string Operation { get; set; }
} }
@@ -274,24 +279,34 @@ namespace Il2CppInspector
// Try to cast each enabled plugin to a specific interface type, and for those supporting the interface, execute the supplied delegate // Try to cast each enabled plugin to a specific interface type, and for those supporting the interface, execute the supplied delegate
// Errors will be forwarded to the error handler // Errors will be forwarded to the error handler
internal static E Try<I, E>(Action<I, E> action) where E : PluginEventInfo, new() internal static E Try<I, E>(Action<I, E> action, [CallerMemberName] string hookName = null) where E : PluginEventInfo, new()
{ {
var eventInfo = new E(); var eventInfo = new E();
var enabledPlugins = AsInstance.ManagedPlugins.Where(p => p.Enabled);
foreach (var plugin in EnabledPlugins) foreach (var plugin in enabledPlugins)
if (plugin is I p) if (plugin.Plugin is I p)
try { try {
// Silently disallow recursion unless [Reentrant] is set on the method
if (plugin.StackTrace.Contains(hookName)) {
var allowRecursion = p.GetType().GetMethod(hookName).GetCustomAttribute(typeof(ReentrantAttribute)) != null;
if (!allowRecursion)
continue;
}
plugin.StackTrace.Push(hookName);
action(p, eventInfo); action(p, eventInfo);
plugin.StackTrace.Pop();
if (eventInfo.FullyProcessed) if (eventInfo.FullyProcessed)
break; break;
} }
catch (Exception ex) { catch (Exception ex) {
// Disable failing plugin // Disable failing plugin
Plugins[plugin.Id].Enabled = false; plugin.Enabled = false;
// Forward error to error handler // Forward error to error handler
eventInfo.Error = new PluginErrorEventArgs { Plugin = plugin, Exception = ex, Operation = typeof(I).Name }; eventInfo.Error = new PluginErrorEventArgs { Plugin = plugin.Plugin, Exception = ex, Operation = hookName };
ErrorHandler?.Invoke(AsInstance, eventInfo); ErrorHandler?.Invoke(AsInstance, eventInfo);
} }

View File

@@ -209,7 +209,7 @@ namespace Il2CppInspectorGUI
MessageBox.Show($"One or more plugin options for {e.Error.Plugin.Name} are invalid: {e.Error.Exception.Message}", "Plugin error"); MessageBox.Show($"One or more plugin options for {e.Error.Plugin.Name} are invalid: {e.Error.Exception.Message}", "Plugin error");
else else
MessageBox.Show($"The plugin {e.Error.Plugin.Name} encountered an error while executing {e.Error.Operation}: {e.Error.Exception.Message}." MessageBox.Show($"The plugin {e.Error.Plugin.Name} encountered an error while executing {e.Error.Operation}: {e.Error.Exception.Message}."
+ Environment.NewLine + Environment.NewLine + "Plugin has been disabled.", "Plugin error"); + Environment.NewLine + Environment.NewLine + "Plugin has been disabled for this session.", "Plugin error");
}; };
PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text); PluginManager.StatusHandler += (s, e) => StatusUpdate(e.Plugin, "[" + e.Plugin.Name + "]\n" + e.Text);