Найти значения аргументов вызова метода в IL - PullRequest
2 голосов
/ 13 мая 2009

У меня есть несколько специальных методов, и я хочу проанализировать их вызовы в скомпилированной сборке.

Пример:

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();
}

Из этого скомпилированного кода я хочу получить значения аргумента {"Следует создать массив", MyClass, "с указанным размером". }. Я попытался использовать Сесил из Mono, и я нашел инструкции для вызова метода «ToDo». Но теперь я запутался, как идентифицировать инструкцию по значению аргумента.

Я знаю, что может быть сложная ситуация, и значение некоторого аргумента не может быть разрешено. Но мне нужно разрешить только постоянные значения - этого достаточно для моей цели.

Спасибо.

EDIT: Метод «ToDo» (и аналогичные) следует использовать в качестве альтернативы комментариям (//, / * ... * /), а после компиляции следует анализировать IL, автоматически генерировать документацию и список задач для конкретной сборки. 1014 *

Ответы [ 2 ]

1 голос
/ 13 мая 2009

Генерация кода несколько сбивает с толку, но может быть выполнена для простых случаев:

компиляции:

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

(добавление "x", чтобы заставить его использовать перегрузку params)

дает

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

Таким образом, вам нужно выполнить синтаксический анализ il, чтобы обнаружить аргументы, помещаемые в массив, сгенерированный компилятором. Эротика, которая хрупка, но может быть достаточной, чтобы сказать:

  1. найти вызов «мой метод».
  2. найти ближайший предыдущий объект newarr
  3. взять все ldstr и ldtoken между ними и предположить, что они являются аргументами.

Это грубо, но может быть достаточно для ваших нужд.

Подход в стиле AOP даст вам то, что вы хотите, в время выполнения , просто инструктируя каждый вызов для сброса значений, но во время компиляции подход, описанный выше, является вашим единственным реалистичным вариантом, учитывая только IL.

Сгенерированный код может сильно отличаться в сборках Release. Вы не сможете обнаружить автоматически сгенерированные массивы стихов, которые кто-то явно создал, сами (которые могут находиться дальше от сайта вызова или даже в другом методе / конструкторе / классе). .

Proviso

Я должен отметить после вашего редактирования, почему вы хотите сделать это, что аннотации на основе атрибутов являются гораздо лучшим решением, я не могу понять, почему вы хотели бы сделать это в методе, когда вы можете напрямую приписать его ...

0 голосов
/ 13 мая 2009

Я не уверен, что вы имеете в виду. Однако обратите внимание, что ваша функция действительно получает только один аргумент: массив. Это то, что вы получаете в IL тоже. Внутри функции, вы можете пройти через массив, чтобы получить его значения:

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