tags:

views:

257

answers:

2

Hi,

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.

A: 

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);
}
Konrad Rudolph
I want analyze IL. The "ToDo" method should be used as alternative to /* comments */. The "ToDo" method must not be ever called, but I want know about the call. I added "EDIT" with explain why I want it.
TcKs
+1  A: 

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

ShuggyCoUk
In release build will not contains "ToDo" call, because "ToDo" method has "[Conditional( DEBUG )]" attribute. So release build is not important for my purpose.I use attributes too, but attribute arguments has several constrains. With method call I can used:void Method_A() { ... }void Method_B() { // ... SrcMan.ToDo( "Should call ", (Action)Method_A, " if flag XYZ is set to ", true ); // ...}The result of the analyzing should be HTML documentation with hyperlink to "Method_A".
TcKs
+1 for the tip, with "newarrobject". I try it.
TcKs
I tried it, and your tip works great. Thanks!
TcKs