Search code examples
c#clrjitcilpostsharp

What modifications postsharp bring to the cilof my method that prevent me from editing the cil code?


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.


Solution

  • 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.