Show / Hide Table of Contents

Using CilTools.BytecodeAnalysis

CilTools.BytecodeAnalysis library reads .NET methods' Common Intermediate Language (CIL) bytecode and converts it into high-level objects or textual CIL representation so they can be easily studied and programmatically processed. The input data for the bytecode analysis is either the raw byte array with bytecode, or the reflection MethodBase object. When the bytecode source is the raw byte array, metadata tokens resolution is not supported; you can only read opcodes and raw operands. For MethodBase objects, you can use both the standard reflection implementation or custom implementations, as long as they support fetching the method body.

Classes are declared in the CilTools.BytecodeAnalysis namespace. The CilTools.BytecodeAnalysis.Extensions namespace provides an alternative syntax via extension methods for reflection classes.

Prerequisites

To use CilTools.BytecodeAnalysis, install CilTools.BytecodeAnalysis NuGet package. You can also build the library from sources or download released binaries in CIL Tools repository. The minimum target framework is .NET Framework 3.5 or .NET Standard 2.0.

Working with instructions

To get the collection of instructions that make up the method body, use GetInstructions(MethodBase). You can also use extension method GetInstructions for MethodBase class. To print the CIL assembler representation of the instruction, call its ToString method. The following example shows how to display all instructions in the method.

using System;
using System.Collections.Generic;
using CilTools.BytecodeAnalysis;
using CilTools.BytecodeAnalysis.Extensions;

class Program
{
    public static void Hello()
    {
        int a = 1;
        int b = 2;
        Console.WriteLine("Hello, World");
        Console.WriteLine("{0} + {1} = {2}",a,b,a+b);
    }

    static void Main(string[] args)
    {
        IEnumerable<CilInstruction> instructions = typeof(Program).GetMethod("Hello").GetInstructions();

        foreach (CilInstruction instr in instructions)
        {
            Console.WriteLine(instr.ToString());
        }
        Console.ReadKey();
    }

}


/* Output:

nop
ldc.i4.1
stloc.0
ldc.i4.2
stloc.1
ldstr      "Hello, World"
call       void [mscorlib]System.Console::WriteLine(string)
nop
ldstr      "{0} + {1} = {2}"
ldloc.0
box        [mscorlib]System.Int32
ldloc.1
box        [mscorlib]System.Int32
ldloc.0
ldloc.1
add
box        [mscorlib]System.Int32
call       void [mscorlib]System.Console::WriteLine(string, object, object, object)
nop
ret
*/

You can also access individual instruction properties, such as OpCode or Operand, to figure out what the instruction does. If the instruction references other member, for example, calls a method, you can use the ReferencedMember property to get the reflection object for that member.

Working with CIL graph

Use the Create(MethodBase) method to get a a graph that represents a flow of control between method's instructions. Working with CIL graph enables you to process branching or exception blocks, which is not possible when working with individual instructions. CIL graph consists of nodes that represents instuctions, with edges representing control flow between them. Use GetNodes() method to get the collection of nodes. You can also disassemble method using the ToText method on CilGraph class, or using the MethodToText(MethodBase) static helper, when you need to output method's CIL code as text.

The following example shows how to construct the CIL graph for the specified method and print its disassembled CIL:

using System;
using CilTools.BytecodeAnalysis;
using CilTools.BytecodeAnalysis.Extensions;

class Program
{
    public static void Foo(int x,int y)
    {
        if(x>y) Console.WriteLine("x>y");
        else if(x==y) Console.WriteLine("x=y");
        else Console.WriteLine("x<y");
    }

    static void Main(string[] args)
    {
        CilGraph graph = typeof(Program).GetMethod("Foo").GetCilGraph();
        Console.WriteLine(graph.ToText());
        Console.ReadKey();
    }
}

/* Output:

.method   public hidebysig static void Foo(
    int32 x,
    int32 y
) cil managed
{
 .maxstack   2
 .locals   init (bool V_0,
    bool V_1)

          nop
          ldarg.0
          ldarg.1
          cgt
          stloc.0
          ldloc.0
          brfalse.s   IL_0001
          ldstr       "x>y"
          call        void [mscorlib]System.Console::WriteLine(string)
          nop
          br.s        IL_0003
 IL_0001: ldarg.0
          ldarg.1
          ceq
          stloc.1
          ldloc.1
          brfalse.s   IL_0002
          ldstr       "x=y"
          call        void [mscorlib]System.Console::WriteLine(string)
          nop
          br.s        IL_0003
 IL_0002: ldstr       "x<y"
          call        void [mscorlib]System.Console::WriteLine(string)
          nop
 IL_0003: ret
}
*/
Back to top CIL Tools (published from sources in GitHub repository).
Generated by DocFX