4

I am using Mono.Cecil to do some analysis and rewriting on an assembly. In some cases this is pretty easy: you can just look right before the call to see where the arguments are loaded:

// Math.Pow(2, 4)
IL_0001:  ldc.r8      00 00 00 00 00 00 00 40 
IL_000A:  ldc.r8      00 00 00 00 00 00 10 40 
IL_0013:  call        System.Math.Pow

However, this gets more complicated when the arguments to the function are themselves complex expressions:

// Math.Pow(2, Math.Abs(Math.Max(17, "123".GetHashCode())));
IL_0001:  ldc.r8      00 00 00 00 00 00 00 40 
IL_000A:  ldc.i4.s    11 
IL_000C:  ldstr       "123"
IL_0011:  callvirt    System.Object.GetHashCode
IL_0016:  call        System.Math.Max
IL_001B:  call        System.Math.Abs
IL_0020:  conv.r8     
IL_0021:  call        System.Math.Pow

In this case, a bunch of stuff happens between when the first argument is loaded and when the second argument is loaded. I'm wondering: does Mono.Cecil expose any tools for finding the IL instructions responsible for pushing each argument?

ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152
  • What do you want to accomplish? The question is a little underdefined because there is not necessarily one instruction per argument (as you have recognized). – usr Feb 13 '16 at 13:27
  • @usr given an instruction which calls a function (e. g. call, newobj), I want to identify the instructions reponsible for pushing each final argument value. In the example above, I want the initial ldc.r8 and the conv.r8. – ChaseMedallion Feb 13 '16 at 13:31

1 Answers1

2

ILSpy is an open source IL to C# decompiler based on Cecil and it can be also used as a library called ICSharpCode.Decompiler. It can transform IL to "IL AST", which I think is exactly what you're asking for.

If you use it like this:

var decompiled = new ILAstBuilder().Build(method, true, new DecompilerContext(module));
var block = new ILBlock(decompiled);
new ILInlining(block).InlineAllVariables();

Then for the following method (compiled in Release mode):

static double F()
{
    return Math.Pow(2, Math.Abs(Math.Max(17, "123".GetHashCode())));
}

The variable block will contain:

ret(call(Math::Pow, ldc.r8(2), conv.r8(call(Math::Abs, call(Math::Max, ldc.i4(17), callvirt(object::GetHashCode, ldstr("123")))))));

Note that I have no experience using ICSharpCode.Decompiler, so there might be a better way to do this.

svick
  • 236,525
  • 50
  • 385
  • 514