4

I am using Mono.Cecil to inject some IL code in auto-implemented property setter. The problem is, i can get reference to it from TypeDefinition.Fields object but when i inject ldfld instruction (after ldarg.0 instruction ofc) with that reference it causes application to break, and CLR invalid program detected exception is raised. I also tried to decompile ILSpy and got exception Mono.Cecil argument out of range exepction in get_Item(int32) method. So it's like i am trying to access backing field before it's created by compiler, but somehow Mono.Cecil can see it when loads assembly.

public class ILTesting
{
    public int Counter { get; set; }
} 

This is how setter looks like before injection:

IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0007: ret

Here is injection code:

var fieldName = "<" + property.Name + ">k__BackingField";
var fieldRef = ilTestType.Fields.Single(field => field.Name == fieldName);
var setterInstruction = property.SetMethod.Body.Instructions;

setterInstructions.Insert(0, Instruction.Create(OpCodes.Brfalse_S, setterInstructions.Last()));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldloc_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Stloc_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ceq));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldc_I4_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ceq));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_1));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldfld, reference));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Nop));

And this is IL i get:

IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0007: ldarg.1
IL_0008: ceq
IL_000a: ldc.i4.0
IL_000b: ceq
IL_000d: stloc.0
IL_000e: ldloc.0
IL_000f: brfalse.s IL_001a

IL_0011: nop
IL_0012: ldarg.0
IL_0013: ldarg.1
IL_0014: stfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0019: nop

IL_001a: ret

So injection code didn't break, reference to backing field exists, but in IL looks like there is not backing field at all.

1 Answers1

1

Problem solved! It's wasn't property, it was the way IL works. I made a full property with code:

private int _counter;

public int Counter
{
     get { return _counter; }
     set
     {
          if (_counter != value)
          {
               _counter = value;
               NotifyUI();
          }
      }
}

I opened assembly in ILSpy and IL code was just like i injected to auto-implemented property. But then i decompiled that IL to see what C# code looks like after decompileing, and code looked like:

private int _counter;

public int Counter
{
     get { return _counter; }
     set
     {
          bool flag = _counter != value; //THIS THING MADE MY LIFE SO HARD FOR A FEW DAYS!
          if (flag)
          {
               _counter = value;
               NotifyUI();
          }
      }
}

So, problem was missing local variable on methods stack frame. After i inserted local variable in method, everything work perfect.

myProperty.SetMethod.Body.Variables.Add(new VariableDefinition(assembly.MainModul.Import(typeof(bool)));