Search code examples
c#cilemit

How to remove the last few segments of Emit IL at runtime


How to remove the last few segments of Emit IL at runtime

I tried to modify some logic in a thousand lines of code, I want to use the rollback IL way to add new logic, avoid changing the previous code.

simple e.g :

I hope to remove the last few segments of Emit IL and then emit new il when isSomeLogic is true.

void Main()
{
    DynamicMethod methodbuilder = new DynamicMethod("Deserialize" + Guid.NewGuid().ToString(), typeof(void), null);
    var il = methodbuilder.GetILGenerator();
    il.Emit(OpCodes.Ldstr, "Hello World");
    Type[] types = new Type[1]{typeof(string)};
    MethodInfo method = typeof(Console).GetMethod("WriteLine", types);
    il.Emit(OpCodes.Call, method);
    il.Emit(OpCodes.Ret);

    // do some thing...
    var isSomeLogic = true;
    if( isSomeLogic ){
        //remove the il OpCodes.Ret and add new logic Emit
        il.Emit(OpCodes.Ret);
    }

    var func = (Action)methodbuilder.CreateDelegate(typeof(Action));
    func();
}

Solution

  • ILGenerator is forwards only. You can't remove opcodes that you've added. Instead, just... don't add them? Alternatively, you can use Label and jump operations; the Ret doesn't need to be at the end of a method; the following is perfectly valid, as long as the stack is at the correct height at the ret, and the stack height is the same for all paths to a particular location.

    • do things
    • jump to X
    • label: Y
    • do more things
    • ret
    • label: X
    • do even more things
    • jump to Y