eim64 eim64 - 11 months ago 129
C# Question

Mono.Cecil: Create Instruction from string

How can i turn strings like these:

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

into Intructions with Operands?

Answer Source

If you now hot to use Mono.Cecil (or Reflection.Emit), the question is more generally about parsing text to code actions.

You have some ways to do that and I can just show you an hint and you can choose your way.

First of all you need some prerequisites (If you IL text is a valid IL code these prerequisites already exist for you). For example, you can't guess what Console.WriteLine is. Console is an assembly, type, method? same questions on WriteLine. And even if we know that WriteLine is a method, which overload we need to choose? and what about generics? So you need set a contract that define for example that the dot is the delimiter and the first section is assembly, the second is namespace, and so on.

For example:

"mscorlib.System.Console.WriteLine(string)" This will be translate to System.Console.WriteLine(string)

After you have a strict contract, you need a few steps (for the WriteLine example):

  1. Resolve the Assembly and get is ModuleDefinition
  2. Resolve and import the Type
  3. Resolve and import the MethodReference describe the requested method
  4. Emit the call

One way to do that, is to keep structure of opcodes and their required action.

For example, we know that call instruction is need to emit call to static method (in most of the cases) so you need to send the operand to ParseStaticCall, there you need to parse the string and emit the call instruction

Pseudo code:

new Dictionary<string, Tuple<OpCode, Action<string>>>
        Tuple.Create<OpCode, Action<string>>

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 is an helper class to find assembly by name and path (can be a constant path). ReferenceFinder is an helper class that find type\method in a specific module.

So you need to create method and ILProccesor for the method body, then for every string you have, you need to separate the instruction opcode form the operand, then look in dictionary for the required action and pass the opcode, the operand as string and the ILProccesor.