pardeike / Harmony

A library for patching, replacing and decorating .NET and Mono methods during runtime
https://www.patreon.com/pardeike
MIT License
5.15k stars 485 forks source link

Problem patching specific method with latest pre-release #564

Closed Zetrith closed 7 months ago

Zetrith commented 8 months ago

Describe the bug Trying to patch a method which ends in a throw results in an exception on the latest pre-release version. The specific method and exception are described below.

To Reproduce I'm not sure what the exact problem is but trying to patch RimWorld's RimWorld.Bill_Production.ShouldDoNow with a postfix (can be an empty one) now results in an exception:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. --->
System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method)
MonoMod.Utils.DynamicMethodDefinition:RimWorld.Bill_Production.ShouldDoNow_Patch1 (RimWorld.Bill_Production):  IL_00ec: shr.un   

The method's body is a chain of ifs and early returns ending with a throw.

The replacement IL from the Harmony log:

### Patch: virtual System.Boolean RimWorld.Bill_Production::ShouldDoNow()
### Replacement: static System.Boolean RimWorld.Bill_Production::RimWorld.Bill_Production.ShouldDoNow_Patch1(RimWorld.Bill_Production this)
IL_0000: Local var 0: System.Int32
IL_0000: Local var 1: System.Boolean
IL_0000: ldc.i4     0
IL_0005: stloc      1 (System.Boolean)
IL_0009: // start original
IL_0009: ldarg.0
IL_000A: ldfld      RimWorld.BillRepeatModeDef RimWorld.Bill_Production::repeatMode
IL_000F: ldsfld     RimWorld.BillRepeatModeDef RimWorld.BillRepeatModeDefOf::TargetCount
IL_0014: beq =>     Label0
IL_0019: ldarg.0
IL_001A: ldc.i4.0
IL_001B: stfld      System.Boolean RimWorld.Bill_Production::paused
IL_0020: Label0
IL_0020: ldarg.0
IL_0021: ldfld      System.Boolean RimWorld.Bill::suspended
IL_0026: brfalse => Label1
IL_002B: ldc.i4.0
IL_002C: br =>      Label10
IL_0031: Label1
IL_0031: ldarg.0
IL_0032: ldfld      RimWorld.BillRepeatModeDef RimWorld.Bill_Production::repeatMode
IL_0037: ldsfld     RimWorld.BillRepeatModeDef RimWorld.BillRepeatModeDefOf::Forever
IL_003C: bne.un =>  Label2
IL_0041: ldc.i4.1
IL_0042: br =>      Label11
IL_0047: Label2
IL_0047: ldarg.0
IL_0048: ldfld      RimWorld.BillRepeatModeDef RimWorld.Bill_Production::repeatMode
IL_004D: ldsfld     RimWorld.BillRepeatModeDef RimWorld.BillRepeatModeDefOf::RepeatCount
IL_0052: bne.un =>  Label3
IL_0057: ldarg.0
IL_0058: ldfld      System.Int32 RimWorld.Bill_Production::repeatCount
IL_005D: ldc.i4.0
IL_005E: cgt
IL_0060: br =>      Label12
IL_0065: Label3
IL_0065: ldarg.0
IL_0066: ldfld      RimWorld.BillRepeatModeDef RimWorld.Bill_Production::repeatMode
IL_006B: ldsfld     RimWorld.BillRepeatModeDef RimWorld.BillRepeatModeDefOf::TargetCount
IL_0070: bne.un =>  Label4
IL_0075: ldarg.0
IL_0076: ldfld      Verse.RecipeDef RimWorld.Bill::recipe
IL_007B: callvirt   Verse.RecipeWorkerCounter Verse.RecipeDef::get_WorkerCounter()
IL_0080: ldarg.0
IL_0081: callvirt   virtual System.Int32 Verse.RecipeWorkerCounter::CountProducts(RimWorld.Bill_Production bill)
IL_0086: stloc.0
IL_0087: ldarg.0
IL_0088: ldfld      System.Boolean RimWorld.Bill_Production::pauseWhenSatisfied
IL_008D: brfalse => Label5
IL_0092: ldloc.0
IL_0093: ldarg.0
IL_0094: ldfld      System.Int32 RimWorld.Bill_Production::targetCount
IL_0099: blt =>     Label6
IL_009E: ldarg.0
IL_009F: ldc.i4.1
IL_00A0: stfld      System.Boolean RimWorld.Bill_Production::paused
IL_00A5: Label5
IL_00A5: Label6
IL_00A5: ldloc.0
IL_00A6: ldarg.0
IL_00A7: ldfld      System.Int32 RimWorld.Bill_Production::unpauseWhenYouHave
IL_00AC: ble =>     Label7
IL_00B1: ldarg.0
IL_00B2: ldfld      System.Boolean RimWorld.Bill_Production::pauseWhenSatisfied
IL_00B7: brtrue =>  Label8
IL_00BC: Label7
IL_00BC: ldarg.0
IL_00BD: ldc.i4.0
IL_00BE: stfld      System.Boolean RimWorld.Bill_Production::paused
IL_00C3: Label8
IL_00C3: ldarg.0
IL_00C4: ldfld      System.Boolean RimWorld.Bill_Production::paused
IL_00C9: brfalse => Label9
IL_00CE: ldc.i4.0
IL_00CF: br =>      Label13
IL_00D4: Label9
IL_00D4: ldloc.0
IL_00D5: ldarg.0
IL_00D6: ldfld      System.Int32 RimWorld.Bill_Production::targetCount
IL_00DB: clt
IL_00DD: br =>      Label14
IL_00E2: Label4
IL_00E2: newobj     System.Void System.InvalidOperationException::.ctor()
IL_00E7: throw
IL_00E8: // end original
IL_00E8: Label10
IL_00E8: Label11
IL_00E8: Label12
IL_00E8: Label13
IL_00E8: Label14
IL_00E8: stloc      1 (System.Boolean)
IL_00EC: call       static System.Void Test.Test::Postfix()
IL_00F1: ldloc      1 (System.Boolean)
DONE

The method is postfixed with Test.Test::Postfix. I guess that the problem is that the replacement doesn't end in a ret which is likely caused by 606ba09.

Expected behavior I expect the method to get patched without an exception.

Runtime environment (please complete the following information):

pardeike commented 7 months ago

Please try current master which fixes this problem. A new release is upcoming but your feedback before using master is appreciated

Zetrith commented 7 months ago

It seems fine now, thanks