I have this method:
public string NestedFoo(SampleClass bar)
{
var1 = "value set in NestedFoo()";
Var2 = "value set in NestedFoo()";
var3 = "value set in NestedFoo()";
Var4 = "value set in NestedFoo()";
AddPerson("From", "NestedFoo", 2);
return Foo();
}
and the cil looks like this (fetched from ildasm and accurate with what my c# cil parser tells me) :
.method public hidebysig newslot virtual final
instance string NestedFoo(class WcfExceptionHandlingPocLib.SampleClass bar) cil managed
{
// Code size 75 (0x4b)
.maxstack 4
.locals init ([0] string V_0)
IL_0000: nop
IL_0001: ldstr "value set in NestedFoo()"
IL_0006: stsfld string WcfExceptionHandlingPocLib.ExceptionHandlingService::var1
IL_000b: ldstr "value set in NestedFoo()"
IL_0010: call void WcfExceptionHandlingPocLib.ExceptionHandlingService::set_Var2(string)
IL_0015: nop
IL_0016: ldarg.0
IL_0017: ldstr "value set in NestedFoo()"
IL_001c: stfld string WcfExceptionHandlingPocLib.ExceptionHandlingService::var3
IL_0021: ldarg.0
IL_0022: ldstr "value set in NestedFoo()"
IL_0027: call instance void WcfExceptionHandlingPocLib.ExceptionHandlingService::set_Var4(string)
IL_002c: nop
IL_002d: ldarg.0
IL_002e: ldstr "From"
IL_0033: ldstr "NestedFoo"
IL_0038: ldc.i4.2
IL_0039: conv.i8
IL_003a: call instance int32 WcfExceptionHandlingPocLib.ExceptionHandlingService::AddPerson(string,
string,
int64)
IL_003f: pop
IL_0040: ldarg.0
IL_0041: call instance string WcfExceptionHandlingPocLib.ExceptionHandlingService::Foo()
IL_0046: stloc.0
IL_0047: br.s IL_0049
IL_0049: ldloc.0
IL_004a: ret
} // end of method ExceptionHandlingService::NestedFoo
I then proceed to parse the IL code I get from the methodbody into a list of IL Instruction :
MethodInfo NestedFooInfo = [...];
byte[] nestedFooIl = NestedFooInfo.GetMethodBody().GetILAsByteArray();
List<ILInstruction> methodILInstructions = IlParser.parseIL(nestedFooIl);
and the ILInstruction class looks like this :
public class ILInstruction
{
public OpCode Code { get; set; }
public byte[] OperandData { get; set; }
}
Once I have this list I proceed to add a nop
instruction at the beginning of the method :
var nopInstruction= new ILInstruction()
{
Code = OpCodes.Nop,
OperandData = null
};
methodILInstructions.Insert(0, nopInstruction);
And transform it back into a byte array :
var newIlCode = IlParser.getIlCode(methodILInstructions);
And then update the method body code :
InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCode);
It works well. And I'm able to add instruction (not only nop
) pretty much everywhere in the code.
When I add a postsharp aspect to this method however it generates a lot additional CIL code.
The NestedFoo
IL code now looks like this:
.method public hidebysig newslot virtual final
instance string NestedFoo(class WcfExceptionHandlingPocLib.SampleClass bar) cil managed
{
// Code size 213 (0xd5)
.maxstack 4
.locals init ([0] string V_0,
[1] string CS$1$1__returnValue,
[2] class [PostSharp]PostSharp.Aspects.MethodExecutionArgs CS$0$2__aspectArgs,
[3] class [PostSharp]PostSharp.Aspects.Internals.Arguments`1<class WcfExceptionHandlingPocLib.SampleClass> CS$0$3__CS$0$3__args,
[4] class [mscorlib]System.Reflection.MethodBase CS$0$4,
[5] class [mscorlib]System.Exception CS$0$5__exception)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: newobj instance void class [PostSharp]PostSharp.Aspects.Internals.Arguments`1<class WcfExceptionHandlingPocLib.SampleClass>::.ctor()
IL_0007: stloc.3
IL_0008: ldloc.3
IL_0009: ldarg.1
IL_000a: stfld !0 class [PostSharp]PostSharp.Aspects.Internals.Arguments`1<class WcfExceptionHandlingPocLib.SampleClass>::Arg0
IL_000f: ldloc.3
IL_0010: newobj instance void [PostSharp]PostSharp.Aspects.MethodExecutionArgs::.ctor(object,
class [PostSharp]PostSharp.Aspects.Arguments)
IL_0015: stloc.2
IL_0016: ldloc.2
IL_0017: ldsfld class [mscorlib]System.Reflection.MethodBase PostSharp.ImplementationDetails._6104080c.'<>z__a_1'::_e
IL_001c: stloc.s CS$0$4
IL_001e: ldloc.s CS$0$4
IL_0020: call instance void [PostSharp]PostSharp.Aspects.MethodExecutionArgs::set_Method(class [mscorlib]System.Reflection.MethodBase)
IL_0025: ldsfld class [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect PostSharp.ImplementationDetails._6104080c.'<>z__a_1'::a4
IL_002a: ldloc.2
IL_002b: callvirt instance void [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect::OnEntry(class [PostSharp]PostSharp.Aspects.MethodExecutionArgs)
.try
{
IL_0030: nop
IL_0031: ldstr "value set in NestedFoo()"
IL_0036: stsfld string WcfExceptionHandlingPocLib.ExceptionHandlingService::var1
IL_003b: ldstr "value set in NestedFoo()"
IL_0040: call void WcfExceptionHandlingPocLib.ExceptionHandlingService::set_Var2(string)
IL_0045: nop
IL_0046: ldarg.0
IL_0047: ldstr "value set in NestedFoo()"
IL_004c: stfld string WcfExceptionHandlingPocLib.ExceptionHandlingService::var3
IL_0051: ldarg.0
IL_0052: ldstr "value set in NestedFoo()"
IL_0057: call instance void WcfExceptionHandlingPocLib.ExceptionHandlingService::set_Var4(string)
IL_005c: nop
IL_005d: ldarg.0
IL_005e: ldstr "From"
IL_0063: ldstr "NestedFoo"
IL_0068: ldc.i4.2
IL_0069: conv.i8
IL_006a: call instance int32 WcfExceptionHandlingPocLib.ExceptionHandlingService::AddPerson(string,
string,
int64)
IL_006f: pop
IL_0070: ldarg.0
IL_0071: call instance string WcfExceptionHandlingPocLib.ExceptionHandlingService::Foo()
IL_0076: stloc.0
IL_0077: br.s IL_0079
IL_0079: ldloc.0
IL_007a: stloc.1
IL_007b: leave.s IL_007d
IL_007d: nop
IL_007e: ldsfld class [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect PostSharp.ImplementationDetails._6104080c.'<>z__a_1'::a4
IL_0083: ldloc.2
IL_0084: callvirt instance void [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect::OnSuccess(class [PostSharp]PostSharp.Aspects.MethodExecutionArgs)
IL_0089: leave.s IL_00d3
} // end .try
catch [mscorlib]System.Exception
{
IL_008b: stloc.s CS$0$5__exception
IL_008d: ldloc.2
IL_008e: ldloc.s CS$0$5__exception
IL_0090: call instance void [PostSharp]PostSharp.Aspects.MethodExecutionArgs::set_Exception(class [mscorlib]System.Exception)
IL_0095: ldsfld class [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect PostSharp.ImplementationDetails._6104080c.'<>z__a_1'::a4
IL_009a: ldloc.2
IL_009b: callvirt instance void [WcfExceptionHandlingPocErrorProcessing]WcfExceptionHandlingPocErrorProcessing.Aspects.MethodBoundaryAspect::OnException(class [PostSharp]PostSharp.Aspects.MethodExecutionArgs)
IL_00a0: ldloc.2
IL_00a1: call instance valuetype [PostSharp]PostSharp.Aspects.FlowBehavior [PostSharp]PostSharp.Aspects.MethodExecutionArgs::get_FlowBehavior()
IL_00a6: switch (
IL_00bf,
IL_00c1,
IL_00bf,
IL_00c1,
IL_00ca)
IL_00bf: rethrow
IL_00c1: ldloc.2
IL_00c2: ldnull
IL_00c3: call instance void [PostSharp]PostSharp.Aspects.MethodExecutionArgs::set_Exception(class [mscorlib]System.Exception)
IL_00c8: leave.s IL_00d3
IL_00ca: ldloc.2
IL_00cb: call instance class [mscorlib]System.Exception [PostSharp]PostSharp.Aspects.MethodExecutionArgs::get_Exception()
IL_00d0: throw
IL_00d1: leave.s IL_00d3
} // end handler
IL_00d3: ldloc.1
IL_00d4: ret
} // end of method ExceptionHandlingService::NestedFoo
And whenever I add an instruction (a nop
for example) I get this:
System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
And I have absolutely no idea why.
I'm pretty sure the parsing deparsing works well, even with the postsharp code because if I don't add the nop
instruction. The byte array i get from the method body and the byte array i get from var newIlCode = IlParser.getIlCode(methodILInstructions);
is exactly the same (tested with IEnumerable.SequenceEqual(...)
.
I just add 1 byte with the nop
and it starts crashing.
Any idea why ?
EDIT:
Why do I want to do that ? => I need to log all modification made to an object. See this How can I keep a ref to an object's state at a given time? for explanation on why I'm following this path.
Why do I mean by "When I add a postsharp aspect to this method" => I apply an attribute to the method to apply this aspect. An aspect is portions of code that will be executed (in this case) before and after the method executes and if an exception arise. In my case the aspect implementation is really long and not really relevant.
PostSharp adds exception handling blocks to your code. You need to properly change offsets/lengths in exception handling clauses in the method header to reflect the added instruction. Since you've added an instruction, these intervals likely point to the middle of instructions and JIT fails to process the method.
This would also happen if you had exception handling in the original code.
See Partition II 25.4 of ECMA-335 for more information.