Find argument values of method call in IL

By : TcKs
Source: Stackoverflow.com
Question!

I have several special methods, and I want analyze they calls in compiled assembly.

Example:

public static class SrcHelper {
    [MySpecialMethod]
    [Conditional( "DEBUG" )]
    public static void ToDo( params object[] info ) {
        /* do nothing */
        /* this method is not called when code is compiled in RELEASE mode */
    }
}
// ... somewhere else in another assembly ...
Array CreateArraySampleMethod( int size ) {
    // This call has only informative character. No functionality is required.
    SrcHelper.ToDo( "Should create array of ", typeof( MyClass ), " with specified size." );
    throw new NotImplementedException();
}

From this compiled code I want get the argument values { "Should create array of ", MyClass, " with specified size." }. I tried use Cecil from Mono, and I found the instructions for call "ToDo" method. But now am I confused how to identify instruction with argument values.

I know, there can be complex situation, and some argument's value can not be resolved. But I need resolve only constant values - it's enough for my purpose.

Thanks.

EDIT: The "ToDo" method (and similar ones) should be used as alternative to comments ( //, /* ... */ ), and after compilation, should be IL analyzed and autogenerated documentation and todo-list for concrete assembly.

By : TcKs


Answers

The code generation is somewhat confusing but can be done for simple cases:

compiling:

public static void Main(string[] args)
{
    Console.WriteLine("", // ignore this argument
       "Should create array of ", typeof(int), " with specified size." "x");
}

(adding "x" to force it to use a params overload)

gives

.method public hidebysig static void Main(string[] args) cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .maxstack 4
    .locals init (
        [0] object[] objArray)
    L_0000: ldstr ""
    L_0005: ldc.i4.4 
    L_0006: newarr object
    L_000b: stloc.0 
    L_000c: ldloc.0 
    L_000d: ldc.i4.0 
    L_000e: ldstr "Should create array of "
    L_0013: stelem.ref 
    L_0014: ldloc.0 
    L_0015: ldc.i4.1 
    L_0016: ldtoken int32
    L_001b: call class [mscorlib]System.Type 
                [mscorlib]System.Type::GetTypeFromHandle(
                    valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0020: stelem.ref 
    L_0021: ldloc.0 
    L_0022: ldc.i4.2 
    L_0023: ldstr " with specified size."
    L_0028: stelem.ref 
    L_0029: ldloc.0 
    L_002a: ldc.i4.3 
    L_002b: ldstr "x"
    L_0030: stelem.ref 
    L_0031: ldloc.0 
    L_0032: call void [mscorlib]System.Console::WriteLine(string, object[])
    L_0037: ret 
}

So you have to do is parse the il to detect the arguments being pushed into the compiler generated array. a heristic that is fragile but might be sufficient it to say:

  1. find call to 'my method'.
  2. find nearest previous newarr object
  3. take all ldstr and ldtoken in between these and assume they are the arguments.

This is rough but may be sufficient for your needs.

An AOP style approach will get you what you want at runtime by simply instrumenting every call to dump the values but at sompile time the approach above is your only realistic option given only the IL.

The code generated may be very different in Release builds, You will be unable to spot the auto generated array verses someone explicitly creating it themselves (which may be further away from the call site or even in a different method/constructor/class.

Proviso

I should note after your edit for why you want to do this that Attribute based annotations are a far better solution, I cannot see why you would want to do this in the method when you can attribute it directly...



I'm not sure what you mean. However, notice that your function really only gets one argument: an array. That's what you get in IL, too. Inside the function, you can walk through the array to get its values:

public static void ToDo( params object[] info ) {
    foreach (object x in info)
        Console.WriteLine(x);
}


If you miss your overlay icons, the regular 'SVN Update' and 'SVN Commit' commands, but instead have a 'SVN Upgrade working copy' command, you should click on it. The reason it appears is that TortoiseSVN has been upgraded and the working copy format has changed. This must from 1.7+ be upgraded manually. This will bring your commands and overlay-icons back.

By : superodde


This video can help you solving your question :)
By: admin