Mono.Cecil: создать инструкцию из строки

Как я могу превратить такие строки:

"call        System.Console.WriteLine" 
"ldstr       \"hello\""

в инструкции с операндами?


person eim64    schedule 30.10.2016    source источник
comment
Я не думаю, что вы можете, Console.WriteLine имеет много перегрузок, поэтому call System.Console.WriteLine неоднозначен. Откуда у тебя эта строка? Что вы на самом деле пытаетесь сделать?   -  person svick    schedule 30.10.2016
comment
@svick я получаю код il от LinqPad5 и пытаюсь создать программу, которая программно заменяет il в методах, используя строки в качестве ilcode.   -  person eim64    schedule 30.10.2016


Ответы (1)


Если вы теперь знаете, как использовать Mono.Cecil (или Reflection.Emit), вопрос в более общем плане касается синтаксического анализа текста для кодирования действий.

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

Прежде всего, вам потребуются некоторые предварительные условия (если ваш IL-текст является действительным кодом IL, эти предварительные условия уже существуют для вас). Например, вы не можете угадать, что такое Console.WriteLine. Console - это сборка, тип, метод? те же вопросы на WriteLine. И даже если мы знаем, что WriteLine — это метод, какую перегрузку нам нужно выбрать? а как же дженерики? Поэтому вам нужно установить контракт, который определяет, например, что точка является разделителем, а первый раздел — сборкой, второй — пространством имен и так далее.

Например:

"mscorlib.System.Console.WriteLine(string)" будет переведено на System.Console.WriteLine(string)

После того, как у вас есть строгий контракт, вам нужно выполнить несколько шагов (для примера WriteLine):

  1. Решите Assembly и получите ModuleDefinition
  2. Разрешить и импортировать Type
  3. Разрешите и импортируйте MethodReference описание запрошенного метода
  4. Вызов

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

Например, мы знаем, что инструкция call нужна для вызова статического метода (в большинстве случаев), поэтому вам нужно отправить операнд в ParseStaticCall, там вам нужно разобрать строку и выдать инструкцию вызова

Псевдокод:

new Dictionary<string, Tuple<OpCode, Action<string>>>
{
    {
        "call",
        Tuple.Create<OpCode, Action<string>>
          (OpCodes.Call,ParseStaticCall)
    }
};


static void ParseStaticCall(Opcpde opcode,
                            string call,
                            ILProcessor processor)
{
    string assembly, namespaceName, type, method;
    int numOfParameters;

    var moduleDefenition = AssemblyResolver.Resolve(assembly).MainModule;

    var methodReference = 
        new ReferenceFinder(moduleDefenition).
            GetMethodReference(typeof (Console),
                md => md.Name == methodName &&
                md.Parameters.Count == numOfParameters);
        processor.Emit(opcode, methodReference);
}

AssemblyResolver — это вспомогательный класс для поиска сборки по имени и пути (может быть постоянным путем). ReferenceFinder — это вспомогательный класс, который находит тип\метод в конкретном модуле.

Таким образом, вам нужно создать метод и ILProccesor для тела метода, затем для каждой строки, которая у вас есть, вам нужно отделить код операции инструкции от операнда, затем найти в словаре требуемое действие и передать код операции, операнд как строку и ILProccesor.

person Dudi Keleti    schedule 30.10.2016