Assembly Round Tripping and much more 13 Aug 2005



Thanks to another few hacks today on Cecil, I can now do funny things. Assembly Round Tripping is one of them. It just mean that I can now load an Assembly with Cecil, and write it back. Without loss of data. Anyway Cecil for the moment does not deal with a lot of data :) Here is a sample code so that you can figure out by yourself:

using Mono.Cecil;

class RoundTrip {

    static void Main ()
    {
        string file = "hello.exe"
        for (int i = 0; i < 10; i++)
            AssemblyFactory.SaveAssembly (AssemblyFactory.GetAssembly (
                file, LoadingType.Aggressive), file, AssemblyKind.Console);
    }
}

Doing this ten times it plain useless. But now you are sure that the assembly is well round tripped.

Interesting huh ? But yeah, a bit useless as it is. If Cecil is able to read and assembly and write it back, it should also be possible to modify it. To go further, I have to assume that you've read the precedent post, that reveals the Hello World example. Now let's read this wonderful piece of code that will modify the Hello World :

using Mono.Cecil;
using Mono.Cecil.Cil;

class Pre_AOP_Era {

    static void Main ()
    {
        string file = "hello.exe"
        IAssemblyDefinition asm = AssemblyFactory.GetAssembly (
            file, LoadingType.Aggressive);

        ITypeDefinition hello = asm.MainModule.Types ["Test.Hello"];
        IMethodDefinition main = hello.Methods [0]; // get the Main method
        ICilWorker worker = main.Body.GetWorker ();
        
        InsertBeforeRetInstruction (
            worker, worker.Create (OpCodes.Ldstr, "And from Jb too!"));
        InsertBeforeRetInstruction (
            worker, worker.Create (OpCodes.Call, typeof (Console).GetMethod (
                    "WriteLine", new Type [] {typeof (string)})));
                    
        AssemblyFactory.SaveAssembly (asm, file, AssemblyKind.Console);
    }
    
    static void InsertBeforeRetInstruction (ICilWorker worker, IInstruction ins)
    {
        IMethodBody body = worker.GetBody ();
        IInstruction ret = body.Instructions [body.Instructions.Count - 1];
        worker.InsertBefore (ret, ins);
    }
}

Basically, I retrieve here the type and a method defined in the precedent example, and using I add two instructions before the last instruction of this method. I just call Console.WriteLine with a new string "And from Jb too!" The ICilWorker interface defines methods to create and manipulate CIL streams. As it is not very complete, I've written here a little method to help in this task.

Unsurprisingly, by invoking the new executable, you'll be greeted twice:

newton:~/Sources/temp jbevain$ mono hello.exe 
Hello from Cecil!
And from Jb too!

This feature of Cecil is a terribly exciting one. It leds to many applications, including the re-write of the core of AspectDNG, the Aspect Weaver Thomas Gil and I are working on.

Don't be desapointed, but once again, this is still not in SVN, but it shows the lasts progress of Cecil. I hope you enjoy it as much as I do :)

Oh, and because he loves pictures, here is one of Sebastien Ros, one of my mentor, and me: