C# Harmony Transpiler null field exception for one of two nearly identical calls

272 Views Asked by At

Here's my code. Question is at the bottom:

namespace DifficultyModNS
{
    [HarmonyPatch(typeof(Boosterpack),nameof(Boosterpack.Clicked))]
    public class OneVillagerChecks
    {
        public static Int32 frequency = 5;
        public static Int32 startChecking = 10;

        static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            foreach (var instruction in instructions)
            {
                if (instruction.opcode == OpCodes.Ldc_I4_5)
                {
                    DifficultyMod.Log("Transpiling const 5 to variable");
                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::frequency"));
                }
                else if (instruction.opcode == OpCodes.Ldc_I4_S && Convert.ToInt32(instruction.operand) == 10)
                {
                    DifficultyMod.Log("Transpiling const 10 to variable");
                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::startChecking"));
                }
                else
                {
                    yield return instruction;
                }
            }
        }
    }
}

The first log message prints. Then just after the second log message there's an exception:

[17:18:44] [Log : difficulty_mod] Transpiling const 5 to variable
[17:18:44] [Log : difficulty_mod] Transpiling const 10 to variable
ArgumentNullException: Invalid argument for ldfld NULL
Parameter name: operand
  at HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) [0x001b4] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspilers () [0x00084] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteImpl () [0x00031] in <4a2409e6eb214388b8d6ab81d407350d>:0 
Rethrow as HarmonyException: IL Compile Error (unknown location)
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteImpl () [0x00382] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Process (MonoMod.Cil.ILContext ilContext, System.Reflection.MethodBase originalMethod) [0x00042] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo, MonoMod.Cil.ILContext ctx) [0x00006] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx) [0x00007] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.Manipulator (MonoMod.Cil.ILContext ctx) [0x00012] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at MonoMod.Cil.ILContext.Invoke (MonoMod.Cil.ILContext+Manipulator manip) [0x00087] in <92bbbc5a09fc417ca7426fbdd31824c2>:0 
  at MonoMod.RuntimeDetour.ILHook+Context.InvokeManipulator (Mono.Cecil.MethodDefinition def, MonoMod.Cil.ILContext+Manipulator cb) [0x00012] in <925d74c1e4e3467a888e7df55b8370c5>:0 
  at (wrapper dynamic-method) MonoMod.RuntimeDetour.ILHook+Context.DMD<MonoMod.RuntimeDetour.ILHook+Context::Refresh>(MonoMod.RuntimeDetour.ILHook/Context)
  at (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Trampoline<MonoMod.RuntimeDetour.ILHook+Context::Refresh>?1194592624(object)
  at HarmonyLib.Internal.RuntimeFixes.StackTraceFixes.OnILChainRefresh (System.Object self) [0x00000] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at MonoMod.RuntimeDetour.ILHook.Apply () [0x00059] in <925d74c1e4e3467a888e7df55b8370c5>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x00047] in <4a2409e6eb214388b8d6ab81d407350d>:0 
// there's more but it's just additional subsystems rethrowing

Why does one of these work but the other one doesn't when the two fields are sitting next to each other in the same class:

yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::frequency"));
yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::startChecking"));

EDIT: I removed the else if for the one that "doesn't work" and I've discovered the exception still happens, after this function exits. So now I guess the question is Why does this return NULL for the operand to Ldfld when the name should exist?

EDIT2: I was using the wrong IL instruction. And the Harmony AccessTools lib is garbage for getting FieldInfo. That was replaced with typeof(OneVillagerChecks).GetField("frequency").

0

There are 0 best solutions below