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. These patches will allow you to receive an (optional) ILContext for the method you're patching as well as the (optional) MethodBase of the original method.

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.
  • 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) { }

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

    // 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) { } // 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