Mono.Cecil automatically implemented property available for backup field

I use Mono.Cecil to inject some IL code into an auto-implemented set of properties. The problem is that I can get a link to it from the TypeDefinition.Fields object, but when I insert the ldfld instruction (after the ldarg.0 instruction) with this link, it causes the application to fail, and the CLR invalid program detected exception is thrown. I also tried decompiling ILSpy and got a Mono.Cecil argument out of range exepction in get_Item(int32) method . So it’s as if I’m trying to access the backup fields before the compiler Mono.Cecil it, but somehow Mono.Cecil can see it when the assembly loads.

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

Here's what the 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 the 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 

Thus, the injection code did not break, the reference to the support field exists, but in IL it seems that there is no support field.

+4
source share
1 answer

The problem is solved! It was not a property; it was an IL method. I made a complete property with code:

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

I opened the assembly in ILSpy and IL , the code was exactly the same as I entered in the automatically implemented property. But then I decompiled this IL to see what the C# code looked like after decompilation, and the code looked like this:

 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(); } } } 

Therefore, the problem was missing a local variable in the methods of the frame stack. After I inserted the local variable into the method, everything works fine.

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

All Articles