The Thousand Nights and a Night of Hacking 13 Aug 2005



Hey world, it's 6am here in Paris, and I'm still in the front of my computer. But this morning, I feel just like happy, excited, and proud. Yeah, and tired too.

I worked all night, and this morning, Cecil have written its first Hello World!. The code is not yet is SVN, it's still full of debug code, but heh, its works here :)

Here is the little application that emits this new and shiny hello world:

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

class Test {

    static void Main ()
    {
        IAssemblyDefinition asm = AssemblyFactory.DefineAssembly (
            "hello", "HelloWorld", TargetRuntime.NET_1_1);

        ITypeDefinition t = asm.MainModule.DefineType (
            "Hello", "Test", TypeAttributes.Class, typeof (object));

        IMethodDefinition meth = t.DefineMethod (
            "Main", MethodAttributes.Static, typeof (void));

        IMethodBody body = meth.DefineBody ();
        ICilWorker cw = body.GetWorker ();
        cw.Emit (OpCodes.Ldstr, "Hello from Cecil!");
        cw.Emit (OpCodes.Call, typeof (Console).GetMethod (
                "WriteLine", new Type [] {typeof (string)}));
        cw.Emit (OpCodes.Ret);

        IMethodDefinition ctor = t.DefineConstructor ();
        body = ctor.DefineBody ();
        cw = body.GetWorker ();
        cw.Emit (OpCodes.Ldarg_0);
        cw.Emit (OpCodes.Call,
            typeof (object).GetConstructor (new Type [0]));
        cw.Emit (OpCodes.Ret);

        asm.EntryPoint = meth;

        AssemblyFactory.SaveAssembly (
            asm, "hello.exe", AssemblyKind.Console);
    }
}

The application is simple, and will looks familiar to everyone that already used System.Reflection.Emit. Of course there are some differences, but let see that later, just notice that Cecil understands both Cecil types, and classics System.Type. Oh, and once again, don't try this at home, it won't work. At least, it's not dangerous. As expected, if you execute this file using Mono, you'll be greeted:

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

Don't expect Cecil to emit more things for the moment. But it's already not that bad. Let me show you another interesting feature. Imagine you need to generate some ponctual code, that you plan neither to keep, nor to save on the disk. The use case for the AssemblyBuilderAccess.Run enum value within System.Reflection.Emit. Let see how to do that!

Add this import:

using SR = System.Reflection;

And remove the last line of the code above, and replace it the following lines:

        SR.Assembly a = AssemblyFactory.CreateReflectionAssembly (
            asm, AssemblyKind.Console);
        
        a.GetType ("Test.Hello").GetMethod (
            "Main", SR.BindingFlags.NonPublic |
            SR.BindingFlags.Static
        ).Invoke (null, new object [0]);

The assembly will be emitted in memory, and loaded as it in an AppDomain (the current one by default). Then you'll be able to work on it like any other assembly. Cool huh ?

So, tasks for tomorrow: clean the code, improve it, extend it, then it will go into SVN. As I'm happy, for no reason, here is a picture of where Cecil is tinkered. Please notice in the bottom right a good old xbox, that used to be famous. May it rest in peace.

Hum, if the CLR and MS ildasm loads my fresh new hello world, it can't be run by lauching it just like another executable. It's not a valid Win32 executable it says. f**k you billy, I'll see that tomorrow, I'm going to sleep.