Найти значения аргументов вызова метода в IL

StackOverflow https://stackoverflow.com/questions/857593

  •  21-08-2019
  •  | 
  •  

Вопрос

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

Пример:

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 "с указанным размером". }.Я попробовал использовать Cecil из Mono и нашел инструкции по вызову метода ToDo.Но теперь я не понимаю, как идентифицировать инструкцию со значениями аргументов.

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

Спасибо.

РЕДАКТИРОВАТЬ:Метод «ToDo» (и подобные ему) следует использовать в качестве альтернативы комментариям ( //, /* ...*/ ), а после компиляции необходимо проанализировать IL и автоматически сгенерировать документацию и список задач для конкретной сборки.

Это было полезно?

Решение

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

компиляция:

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

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

дает

.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 между ними и предположите, что они являются аргументами.

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

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

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

условие

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

Другие советы

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

public static void ToDo( params object[] info ) {
    foreach (object x in info)
        Console.WriteLine(x);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top