Skip to content

ILManipulators

Aaron Robinson edited this page Mar 8, 2021 · 5 revisions

In addition to the normal Harmony transpiler, HarmonyX provides the additional ILManipulator patch type, which enables the use of common MonoMod helpers such as ILCursor. There are several optional parameters you can use in these patches:

  • ILContext - An ILContext for the method you're manipulating. Use this to interact with the rest of the MonoMod library.
  • MethodBase - The MethodBase of the original method you're manipulating.
  • ILLabel - A label to the end of the original. How this label is intended to be used will be discussed in a later section. Not much use in Reverse Patches.

ILManipulators are meant as an alternative to Transpilers, and are fully integrated and compatible with everything else that Harmony provides.

Warnings

ILManipulators run after all other patch types have been applied (this is due to a technical limitation and making sure existing patches work as expected). This can lead to some easy errors when you start with ILManipualtors, and you should keep in mind these conditions when designing your patches.

Because of how prefixes, postfixes, and finalizers are applied to methods, they will be inside the method body by the time your patch runs. This has several repercussions when it comes to what you write:

  • All ret opcodes in the method will have become br opcodes that branch to the beginning of where the postfixes and finalizers run.
    • This means that you should be careful about adding ret opcodes yourself, as that will skip all postfixes and finalizers applied to the method.
    • You in order to avoid emitting ret opcodes yourself, you can use the optional ILLabel parameter to unconditionally branch to where postfixes and finalizers begin.
  • Since Harmony may have changed the method before your patch recieves it, the IL may not be exactly the same as what ILSpy/dnSpy/dotPeek tell you.
    • If you need to see the exact IL Harmony generates, enable the IL log channel in your configs.

Usage

ILManipulators can be used in all of the same contexts as Transpilers, with an attribute, with the correct name, or as part of a Reverse Patch.

ILManipulators should always return void.

Examples for each usage:

public class ExampleClassToBePatched
{
    public string ExampleMethod()
    {
        return "example string";
    }
}

[HarmonyPatch(typeof(ExampleClassToBePatched), "ExampleMethod")]
public class PatchClass
{
    // With the correct naming scheme
    public static void ILManipulator(ILContext il, MethodBase original, ILLabel retLabel) { }

    // With the correct attribute applied to the method
    [HarmonyILManipulator]
    public static void SomeOtherILManipulator(ILContext ctx, MethodBase orig) { } // parameter names can be anything, all parameters are optional

    // As part of a reverse patch
    [HarmonyReversePatch]
    [MethodImpl(MethodImplOptions.NoInlining)] // make sure the method is never inlined so the patch as actually called in our code
    public static string ExampleMethodReverse()
    {
        void Manipulator(ILContext il) { } // manipulator method can be named anything, and all parameters are optional

        Manipulator(null); // get rid of compiler warning about unused method
        return default(string); // get rid of compiler error about no value being returned
    }
}

TODO: Write comprehensive examples unique to HarmonyX

External references

To see more details about implementation of MonoMod's ILContext and its helpers to manipulate it, please reference:

TODO: Find more resources for MonoMod's helpers