Mono.Cecil 0.9.2

Posted by Jb Evain Wed, 05 May 2010 15:28:00 GMT

I tagged Mono.Cecil 0.9.2 at the beginning of the week in Cecil’s github. It’s a bug fix release, exactly two weeks after the release of 0.9.1.

It’s fast, stable, and shiny, thanks to all the contributors and testers of the 0.9 line. It’s safe to say that users of the 0.6 line of code are urged to update to this version, and report issues, if any.

parameterof, propertyof, methodof 8

Posted by Jb Evain Wed, 05 May 2010 15:09:00 GMT

Every C# programmer knows about the keyword typeof, in essence, it returns an instance of System.Type for a given type. It’s used everywhere you have to do reflection on a type known at compilation time. You could achieve pretty much the same with the methods GetType(string) of both Module and Assembly, but the nice thing about typeof is that it’s refactoring proof.

Now, every C# programmer also knows that typeof is the only keyword with such abilities. There’s no parameterof, propertyof, methodof, or eventof to access other metadata members. The web is filled with ways to emulate a type safe reflection though. Lately, expression trees helped a lot. This post presents a proof of concept piece of code for a different approach.

Considering the following types:

public static class Parameter {

    public static ParameterInfo Of<T> (T value)
    {
        return null;
    }
}

public static class Property {

    public static PropertyInfo Of<T> (T value)
    {
        return null;
    }

    public static PropertyInfo Of (MethodInfo method)
    {
        const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;

        foreach (var property in method.DeclaringType.GetProperties (all))
            if (method.Equals (property.GetGetMethod (true)))
                return property;

        return null;
    }
}

public static class Method {

    public static MethodInfo Of<T> (Func<T> value)
    {
        return null;
    }

    public static MethodInfo Of<T1, TRet> (Func<T1, TRet> value)
    {
        return null;
    }

    public static MethodInfo Of (Action value)
    {
        return null;
    }

    public static MethodInfo Of<T> (Action<T> value)
    {
        return null;
    }
}

They are basically just stubs, but let’s use them anyway:

class Program {

    static void Main (string [] args)
    {
        ParameterInfo parameter = Parameter.Of (args);

        Console.WriteLine ("Parameter of args: {0}", parameter);

        PropertyInfo rank = Property.Of (args.Rank);

        Console.WriteLine ("Property of rank: {0}", rank);

        MethodInfo foo = Method.Of<int> (Foo);

        Console.WriteLine ("Foo method: {0}", foo);

        MethodInfo bar = Method.Of<int> (Bar);

        Console.WriteLine ("Bar method: {0}", bar);
    }

    static int Foo ()
    {
        return 42;
    }

    static void Bar (int b)
    {
        return;
    }
}

Of course if you run the code as is, everything will be null. What I did is that I wrote a little Cecil rewriter that will detect the calls to the different Of methods, and rewrite them to something that will give you the expected results.

So for instance, the Parameter.Of call is compiled as:

ldarg.0
call class [mscorlib]System.Reflection.ParameterInfo Parameter::Of<string[]>(!!0)

But is rewritten in two parts. First we emit at the beginning of the method a way to retrieve of the ParameterInfo of the current method:

ldtoken method void Program::Main(string[])
call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
callvirt instance class [mscorlib]System.Reflection.ParameterInfo[] [mscorlib]System.Reflection.MethodBase::GetParameters()
stloc $reflection_parameters

And we simple replace the Of call with a load from the $reflection_parameters array:

ldloc $reflection_parameters
ldc.i4 0
ldelem.ref

For methods it’s a bit different, they are compiled as:

ldnull
ldftn int32 Program::Foo()
newobj instance void class [mscorlib]System.Func`1<int32>::.ctor(object,native int)
call class [mscorlib]System.Reflection.MethodInfo Method::Of<int32>(class [mscorlib]System.Func`1<!!0>)

And we rewrite them to remove the creation of a delegate:

ldtoken method int32 Program::Foo()
call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
castclass  [mscorlib]System.Reflection.MethodInfo

For properties, it’s exactly the same, but we have to inject a call to a method that will retrieve the PropertyInfo the get method belongs to.

If you’re interested you can read the rewriter code. It’s pretty simple, but is nowhere complete, and I doubt anyone would use it anyway, but it’s still a pretty cool hack.

Mono.Cecil 0.9.1

Posted by Jb Evain Mon, 19 Apr 2010 15:38:00 GMT

I pushed to my github repository an update for Mono.Cecil. It contains a week worth of fixes reported by the folks who ported their code to use the new API. The main addition for this release is the support for complex types in custom attributes, which was pretty much the only known thing that was not supported in 0.9, metadata wise.

Thanks to everyone that took the time to update to 0.9 and report issues.

Mono.Cecil 0.9 "cecil/light" 9

Posted by Jb Evain Mon, 12 Apr 2010 18:28:00 GMT

Statue

I started working on Mono.Cecil during the fall of 2004. In its current incarnation, it served me and a lot of people very well. But looking at it now, it aged quite a bit. The code still compiles on .net 1.1, is using old conventions, doesn’t have a real test suite, is quite memory hungry, and is not that optimized. Which doesn’t prevent it to be a useful and wide used library, but looking back; I could have done a lot of things differently.

And doing things differently is basically what I’ve been doing for the past two years in my free time. What originally started as a refactoring of Mono.Cecil for the decompiler, ended up as a rewrite from the ground up. And today I’m excited to make public what is the next version of Cecil, which I’ve been fondly calling “cecil/light”.

Let’s start with a warning; this version contains breaking changes with the previous API. I didn’t promise API stability for the previous code, but this iteration of Mono.Cecil, tagged 0.9, is a huge step towards 1.0 and API stability.

But let’s focus for a while on the bright and new side. Mono.Cecil 0.9 comes with:

  • A cleaned and genericized API, I took this opportunity to clean some parts I hated in the old API.
  • A smaller and easier to maintain C#3 code base (Mono.Cecil 0.9 compiled with optimizations by csc is about 250k against almost 400k for 0.6) which only requires a .net 2.0 compatible runtime.
  • A test suite which is very easy to augment.
  • Better support for pdb and mdb files and strong name assemblies.
  • Complete support for PE32+ assemblies.
  • Bug fixes that weren’t possible without large changes in the old code.
  • Less memory consumption.
  • Lazy loading of every metadata element.
  • Speed and optimizations.
  • Complete Silverlight support.
  • A beginning of documentation on a wiki.
  • A collection of extension methods to add features to Cecil when they’re not necessary to the core assembly.

I ported a few of my projects to this version of Cecil already, and it shows great results. I didn’t spend more than four hours per project to adjust the code in a branch. There’s a migration page on the wiki to help you. If it doesn’t answer your question, reach us on the mono-cecil group.

I took special care in testing this version, and waited to have something stable to make it public, but just like every rewrite, you might face bugs or regressions. I’m confident it won’t take long before it gets really stable. A few people had early access to this code base, and ensured it was at least working as well as the previous version, if not better most of the time.

I moved the development of this version of Mono.Cecil to github while I’ll be maintaining the previous code in the Mono tree, as it’s still used by a lot of tools there, that I haven’t migrated yet.

We’ll also be working on writing more documentation for 1.0, both as a serie of HOWTOs, and as a more traditional class library documentation. But everyone that has already used Cecil should be up to speed promptly.

Thanks to everyone that helped, whether they knew it or not. Many thanks to Rodrigo B. de Oliveira, Carlo Kok, Sébastien Pouliot, and all the contributors to Mono.Cecil. Thanks also to Jeroen Frijters for his fantastic work on his IKVM.Reflection.Emit assembly in which I sometimes took inspiration. Enjoy!

Picture by law_keven some rights reserved

Older posts: 1 2 3 ... 15