Added a couple extension methods for Mono.Cecil, fixed bug in ReplaceBodyWith

This commit is contained in:
bbradson
2024-04-27 20:46:07 +02:00
parent 5656c3e80b
commit b5cda73ecd
10 changed files with 115 additions and 25 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+113 -23
View File
@@ -10,6 +10,7 @@ using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.Cecil.Rocks; using Mono.Cecil.Rocks;
using Mono.Collections.Generic; using Mono.Collections.Generic;
using MonoMod.Utils;
using Verse; using Verse;
using CallSite = Mono.Cecil.CallSite; using CallSite = Mono.Cecil.CallSite;
@@ -100,6 +101,81 @@ public static class CecilExtensions
return variableDefinition; return variableDefinition;
} }
public static Collection<VariableDefinition> GetLocalVariables(this ILProcessor ilProcessor)
=> ilProcessor.Body.Variables;
public static VariableDefinition FirstLocalVariable(this ILProcessor ilProcessor, Type type)
=> ilProcessor.FirstLocalVariableOrDefault(type) ?? ThrowForNoMatchingLocalVariable(ilProcessor, type);
public static VariableDefinition? FirstLocalVariableOrDefault(this ILProcessor ilProcessor, Type type)
{
Guard.IsNotNull(ilProcessor);
Guard.IsNotNull(type);
var variables = ilProcessor.GetLocalVariables();
for (var i = 0; i < variables.Count; i++)
{
if (variables[i].VariableType.Is(type))
return variables[i];
}
return null;
}
[DoesNotReturn]
private static VariableDefinition ThrowForNoMatchingLocalVariable(ILProcessor ilProcessor, Type type)
=> ThrowHelper.ThrowInvalidOperationException<VariableDefinition>(
$"No local variable of type '{type.FullName}' found in method '{ilProcessor.Body.Method.FullName}'.");
public static Collection<ParameterDefinition> GetParameters(this ILProcessor ilProcessor)
=> ilProcessor.Body.Method.Parameters;
public static ParameterDefinition GetParameter(this ILProcessor ilProcessor, string name)
=> ilProcessor.GetParameterOrDefault(name) ?? ThrowForNoMatchingParameter(ilProcessor, name);
public static ParameterDefinition? GetParameterOrDefault(this ILProcessor ilProcessor, string name)
{
Guard.IsNotNull(ilProcessor);
Guard.IsNotNull(name);
var parameters = ilProcessor.GetParameters();
for (var i = 0; i < parameters.Count; i++)
{
if (parameters[i].Name == name)
return parameters[i];
}
return null;
}
[DoesNotReturn]
private static ParameterDefinition ThrowForNoMatchingParameter(ILProcessor ilProcessor, string name)
=> ThrowHelper.ThrowInvalidOperationException<ParameterDefinition>(
$"No parameter named '{name}' found in method '{ilProcessor.Body.Method.FullName}'.");
public static ParameterDefinition FirstParameter(this ILProcessor ilProcessor, Type type)
=> ilProcessor.FirstParameterOrDefault(type) ?? ThrowForNoMatchingParameter(ilProcessor, type);
public static ParameterDefinition? FirstParameterOrDefault(this ILProcessor ilProcessor, Type type)
{
Guard.IsNotNull(ilProcessor);
Guard.IsNotNull(type);
var parameters = ilProcessor.GetParameters();
for (var i = 0; i < parameters.Count; i++)
{
if (parameters[i].ParameterType.Is(type))
return parameters[i];
}
return null;
}
[DoesNotReturn]
private static ParameterDefinition ThrowForNoMatchingParameter(ILProcessor ilProcessor, Type type)
=> ThrowHelper.ThrowInvalidOperationException<ParameterDefinition>(
$"No parameter of type '{type}' found in method '{ilProcessor.Body.Method.FullName}'.");
public static void InsertAt(this ILProcessor ilProcessor, int index, Instruction instruction) public static void InsertAt(this ILProcessor ilProcessor, int index, Instruction instruction)
=> ilProcessor.instructions.Insert(index, instruction); => ilProcessor.instructions.Insert(index, instruction);
@@ -349,23 +425,32 @@ public static class CecilExtensions
replacement.Name, replacement.GetParameters().GetTypes()); replacement.Name, replacement.GetParameters().GetTypes());
} }
var newMethodReference = module.ImportReference(replacement); var replacementMethodReference = module.ImportReference(replacement);
var newBody = newMethodReference.Resolve().Body; var replacementBody = replacementMethodReference.Resolve().Body;
newBody.SimplifyMacros(); replacementBody.SimplifyMacros();
MethodBodyRocks.ComputeOffsets(newBody); MethodBodyRocks.ComputeOffsets(replacementBody);
var targetVariables = targetBody.Variables;
var replacementVariables = replacementBody.Variables;
targetVariables.Clear();
foreach (var variable in replacementVariables)
targetVariables.Add(new(module.ImportReference(variable.VariableType, replacementMethodReference)));
var targetInstructions = targetBody.instructions; var targetInstructions = targetBody.instructions;
targetInstructions.Clear(); targetInstructions.Clear();
foreach (var instruction in newBody.instructions) var replacementInstructions = replacementBody.instructions;
foreach (var instruction in replacementInstructions)
{ {
targetInstructions.Add(new(instruction.OpCode, instruction.Operand switch targetInstructions.Add(new(instruction.OpCode, instruction.Operand switch
{ {
FieldReference field => module.ImportReference(field, newMethodReference), FieldReference field => module.ImportReference(field, replacementMethodReference),
MethodReference method => module.ImportReference(method, newMethodReference), MethodReference method => module.ImportReference(method, replacementMethodReference),
TypeReference type => module.ImportReference(type, newMethodReference), TypeReference type => module.ImportReference(type, replacementMethodReference),
ParameterReference parameter => targetBody.GetParameter(parameter.Index), ParameterReference parameter => targetBody.GetParameter(parameter.Index),
VariableDefinition variable => targetBody.GetVariable(replacementVariables.IndexOf(variable)),
_ => instruction.Operand _ => instruction.Operand
})); }));
} }
@@ -378,34 +463,39 @@ public static class CecilExtensions
instruction.Operand = targetInstructions.First(ti => ti.Offset == operand.Offset); instruction.Operand = targetInstructions.First(ti => ti.Offset == operand.Offset);
} }
targetBody.Variables.Clear(); targetBody.MaxStackSize = replacementBody.MaxStackSize;
foreach (var variable in newBody.Variables)
targetBody.Variables.Add(new(module.ImportReference(variable.VariableType, newMethodReference)));
targetBody.MaxStackSize = newBody.MaxStackSize;
targetBody.ExceptionHandlers.Clear(); targetBody.ExceptionHandlers.Clear();
foreach (var exceptionHandler in newBody.ExceptionHandlers) foreach (var exceptionHandler in replacementBody.ExceptionHandlers)
{ {
var handler = new ExceptionHandler(exceptionHandler.HandlerType) var handler = new ExceptionHandler(exceptionHandler.HandlerType)
{ {
TryStart = exceptionHandler.TryStart, TryStart = FindInTarget(exceptionHandler.TryStart),
TryEnd = exceptionHandler.TryEnd, TryEnd = FindInTarget(exceptionHandler.TryEnd),
FilterStart = exceptionHandler.FilterStart, FilterStart = FindInTarget(exceptionHandler.FilterStart),
HandlerStart = exceptionHandler.HandlerStart, HandlerStart = FindInTarget(exceptionHandler.HandlerStart),
HandlerEnd = exceptionHandler.HandlerEnd HandlerEnd = FindInTarget(exceptionHandler.HandlerEnd)
}; };
if (exceptionHandler.CatchType is { } catchType) if (exceptionHandler.CatchType is { } catchType)
handler.CatchType = module.ImportReference(catchType, newMethodReference); handler.CatchType = module.ImportReference(catchType, replacementMethodReference);
targetBody.ExceptionHandlers.Add(handler); targetBody.ExceptionHandlers.Add(handler);
} }
targetBody.Method.ImplAttributes = newBody.Method.ImplAttributes; targetBody.Method.ImplAttributes = replacementBody.Method.ImplAttributes;
targetBody.OptimizeMacros(); targetBody.OptimizeMacros();
newBody.OptimizeMacros(); replacementBody.OptimizeMacros();
Instruction? FindInTarget(Instruction? replacementInstruction)
{
if (replacementInstruction is null)
return replacementInstruction;
var index = replacementInstructions.IndexOf(replacementInstruction);
return (uint)index > (uint)targetInstructions.Count ? replacementInstruction : targetInstructions[index];
}
} }
public static MethodDefinition GetMethod(this ILProcessor ilProcessor) => ilProcessor.Body.Method; public static MethodDefinition GetMethod(this ILProcessor ilProcessor) => ilProcessor.Body.Method;
+2 -2
View File
@@ -29,11 +29,11 @@ namespace FisheryLib;
[PublicAPI] [PublicAPI]
public static class FisheryLib public static class FisheryLib
{ {
public const decimal VERSION = 0.512M; public const decimal VERSION = 0.6M;
public static decimal CurrentlyLoadedVersion { get; } = VERSION; public static decimal CurrentlyLoadedVersion { get; } = VERSION;
public const string VERSION_STRING = "0.5.1.2"; public const string VERSION_STRING = "0.6";
// public static string GetVersion() // public static string GetVersion()
// { // {