// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Emit;
using System.Xml;
namespace PerformanceFish;
public static class AnalyzerFixes
{
public static void Patch()
{
var modBase = AccessTools.Constructor(Reflection.Type("PerformanceAnalyzer", "Analyzer.Modbase"), new[] { typeof(ModContentPack) });
if (modBase is null)
return /*false*/;
var success = PerformanceFishMod.Harmony.Patch(modBase, postfix: new(methodof(XmlParser.CollectXmlData))) != null;
var methodTransplanting = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.MethodTransplanting", "UpdateMethod",
new[] { typeof(Type), typeof(MethodInfo) });
if (methodTransplanting is null
|| PerformanceFishMod.Harmony.Patch(methodTransplanting, transpiler: new(methodof(UpdateMethod_Transpiler))) is null)
{
success = false;
}
var panelLogs = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Panel_Logs", "RightClickDropDown");
if (panelLogs is null
|| PerformanceFishMod.Harmony.Patch(panelLogs, postfix: new(methodof(PanelLogs_Postfix))) is null)
{
success = false;
}
return /*success*/;
}
///
/// yes this is literally a prefix
///
public static CodeInstructions UpdateMethod_Transpiler(CodeInstructions codes, MethodBase method)
{
yield return FishTranspiler.ArgumentAddress(method, "meth");
yield return FishTranspiler.Call(UpdateMethod_Prefix);
foreach (var code in codes)
yield return code;
}
public static void UpdateMethod_Prefix(ref MethodInfo meth)
{
if (AccessTools.EnumeratorMoveNext(meth) is { } moveNext)
meth = moveNext;
}
public static IEnumerable PanelLogs_Postfix(IEnumerable values, object log)
{
var method = ProfileLog_meth(log) as MethodInfo;
var returned = false;
foreach (var value in values)
{
yield return value;
if (IsValidMethodForOverriding(method) && !returned && value.Label == "Profile the internal methods of")
{
returned = true;
yield return new("Profile overriding methods of",
() => PatchOverridesForMethod(method, (Category)GUIController_CurrentCategory()));
}
}
}
private static bool IsValidMethodForOverriding([NotNullWhen(true)] MethodInfo? info)
=> info is not null
&& (info.IsVirtual || info.IsAbstract || info.GetBaseDefinition().DeclaringType != info.DeclaringType);
public enum Category
{
Settings,
Tick,
Update,
GUI,
Modder
}
public static void PatchOverridesForMethod(MethodInfo method, Category category)
{
if (OverridingMethodUtility.PatchedOverrides.Contains(method))
{
var signature = Utility_GetSignature(method, true);
Messages.Message("Have already patched overrides for the method " + signature, MessageTypeDefOf.CautionInput, false);
Utility_Warn("Trying to repeat patching already profiled overrides of method - " + signature);
}
else
{
PatchOverridesForMethodFull(method, category);
}
}
private static void PatchOverridesForMethodFull(MethodInfo method, Category category)
{
try
{
OverridingMethodUtility.Generate(method, category);
/*Task.Factory.StartNew(delegate
{
try
{
Modbase_Harmony().Patch(method, null, null, OverridingMethodUtility.OverrideProfiler);
}
catch (Exception e2)
{
Utility_ReportException(e2, "Failed to patch overriding methods for " + Utility_GetSignature(method, false));
}
});*/
}
catch (Exception e)
{
Utility_ReportException(e, "Failed to set up state to patch overriding methods");
OverridingMethodUtility.PatchedOverrides.Remove(method);
}
}
public static class OverridingMethodUtility
{
public static HashSet PatchedOverrides = new();
public static void ClearCaches() => PatchedOverrides.Clear();
public static void Generate(MethodInfo method, Category category)
{
PatchedOverrides.Add(method);
var name = $"{method.DeclaringType}:{method.Name}-overrides";
var methods = new HashSet();
var subClasses = method.DeclaringType.AllSubclasses();
foreach (var subClass in subClasses)
{
var overridingMethod = Array.Find(subClass.GetMethods(AccessTools.allDeclared), m => m.GetBaseDefinition() == method.GetBaseDefinition());
if (overridingMethod is null || overridingMethod.IsAbstract)
continue;
methods.Add(overridingMethod);
//Log.Message($"Added method {Utility_GetSignature(overridingMethod, true)} for profiling");
}
var type = DynamicTypeBuilder_CreateType(name, methods);
var entry = Entry_Create(name, (int)category, type, false, true);
GUIController_AddSpecificEntry((int)category, entry, type);
GUIController_SwapToEntry(name);
}
}
public static AccessTools.FieldRef