Possible Duplicate:
Does Mono.Cecil take care of branches etc location?
I'm working on a project that requires me to insert a static method call after certain methods are called in my app. I'm using Mono Cecil for it, and for the most part everything works fine. However, I'm running into some cases where branch offsets are getting mangled when the updated assembly is written to disk. Here's what my code looks like:
public void InstrumentMethod(MethodDefinition method) { var processor = method.Body.GetILProcessor(); var instr = GetStaticInstrumentMethod(); var instrument = method.Module.Import(instr); var call = method.Body.Instructions.First(i => i.IsCallImInterestedIn()); processor.InsertBefore(call, instrument); }
So, 90% of the time this code work with no issues - the target method is instrumented with my extra call and everything works fine.
The other 10% of the time, the instrumented method can't be executed, and I get errors trying to look at it in ILSpy. The ILSpy error is:
ICSharpCode.Decompiler.DecompilerException: Error decompiling System.Object Savo.Utilities.Mapping.ReflectionMapper::MapTo(System.Object,System.Object) ---> System.NullReferenceException: Object reference not set to an instance of an object. at ICSharpCode.Decompiler.ILAst.ILAstBuilder.c__DisplayClass33.b__26(ByteCode b) at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate) at ICSharpCode.Decompiler.ILAst.ILAstBuilder.ConvertLocalVariables(List`1 body) at ICSharpCode.Decompiler.ILAst.ILAstBuilder.StackAnalysis(MethodDefinition methodDef) at ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody(IEnumerable`1 parameters) at ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable`1 parameters) --- End of inner exception stack trace --- at ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable`1 parameters) at ICSharpCode.Decompiler.Ast.AstBuilder.CreateMethod(MethodDefinition methodDef) at ICSharpCode.ILSpy.CSharpLanguage.DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput) at ICSharpCode.ILSpy.TextView.DecompilerTextView.c__DisplayClass10.b__f()
I've decompiled the code with ildasm, and I'm noticing that there appear to be issues with how the offsets are updated when the assembly is saved. I haven't done anything with the offsets of the call (tried to set them manually but saw the same issues). The pre-instrumentation disassembly for one of my problem methods looks like this:
IL_000e: br.s IL_005e
IL_0010: ldloca.s CS$5$0000
IL_0012: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class MyThing>::get_Current()
IL_0017: stloc.0
IL_0018: nop
... business logic ...
IL_005d: nop
IL_005e: ldloca.s CS$5$0000
IL_0060: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class MyThing>::MoveNext()
IL_0065: stloc.3
IL_0066: ldloc.3
IL_0067: brtrue.s IL_0010
IL_0069: leave.s IL_007a
When I look at the instrumented disassembly, the line that corresponds to IL_0067 in this example comes out as:
IL_00bc: brtrue.s IL_0129
which can't be compiled since IL_0129 doesn't exist in the current method.
I have noticed, but I haven't proven yet that this only occurs around iterators. The C# code for the two cases where this happens so far have both been foreach loops, and the incorrect branch targets both seem to be related to the end condition of the foreach loops.
So, my question is: do I need to manually manage the offset references, or is there something else I'm doing wrong?