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.
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)));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.