Projects using Cecil 2
A month ago, Scott had the good idea to start a page on the Mono.Cecil google group, to try to list the different projects out there that are using Cecil. The page is wisely named Projects using Cecil. I’ve tried to complete it by myself, but I’m for sure missing some of my users.
So if you’re using Cecil, that would be cool to have you listed there. Feel free to change the page yourself, or to drop a name, a summary and if possible a link in the comments.
For bonus points, you can describe what you use Cecil for.
Annotating objects 3
Two weeks ago, I’ve borrowed a concept I’ve first seen in Boo to use it in Cecil.
As the title of this entry kindly suggests, it’s about something called annotations. The need for them came from my work on the linker. Before using annotations, to decide which elements to link, I’ve re-created a tree of objects called markers. For instance, to mark a method, I used to have a class similar to this one:
public class MethodMarker : Marker {
MethodDefinition method;
}
When it was time to sweep methods in a type, I was checking if the method was found in the markers of a type, and if not, I knew I could safely remove it. That worked well, but I was basically re-creating a parallel object tree to the Cecil one, only to support a few more information.
Here came to me the idea of reusing the annotations concept. Let’s have a look at a new interface:
namespace Mono.Cecil {
using System.Collections;
public interface IAnnotationProvider {
IDictionary Annotations { get; }
}
}
I’ve ensured that every important type like TypeDefinition or MethodDefinition in Cecil implement this type, et voilà , I’m now able to randomly tag them, with whatever data I want.
To keep up with the example of the linker, instead of creating a tree of markers, I now simply annotate a method as «marked», and when it’s time to sweep the unused one, the check is now very simple.
Well, that’s pretty useful to me, hopefully it will be useful to you too.
UPDATE:
Following the excellent suggestion from Bertrand, I’ve changed the way the types implement IAnnotationProvider to an explicit implementation, and it looks really better.
About an inherent limitation, and a possible solution 2
The linker is a static tool. It uses Mono.Cecil to analyze the assemblies, modify them, and save them back in a linked form.
That means that the linker can only know about the assemblies and the types that are statically referenced. You already see where I’m going to. Mono, just like .net, provides very interesting, and also widely used, ways to dynamically load assemblies and types. The common use case is a plug-in framework for instance, where assemblies are loaded when asked to.
As an example, let’s have a look to a piece of code I’ve faced when working on linking MoMa:
static Socket() {
Assembly ass;
try {
ass = Assembly.Load (Consts.AssemblyMono_Posix);
} catch (FileNotFoundException) {
return;
}
unixendpointtype=ass.GetType("Mono.Posix.UnixEndPoint");
Type[] arg_types=new Type[1];
arg_types[0]=typeof(string);
ConstructorInfo cons=unixendpointtype.GetConstructor(arg_types);
object[] args=new object[1];
args[0]="nothing";
unixendpoint=cons.Invoke(args);
}
From System.Net.Sockets.Socket in System.dllThis code is fairly simple to understand. The the Socket class constructor will be called, the assembly Mono.Posix will be loaded, and an instance of the type Mono.Posix.UnixEndPoint will be created, and its constructor will be invoked.
That’s the marvel of the reflection API. But as you can see, everything here is dynamic. The assembly System.dll contains no reference to the Mono.Posix.UnixEndPoint type, and no reference to the assembly Mono.Posix.
The linker can take two different kind of input. The first one is of course an assembly. It’s obvious what it does, the linker will link everything necessary for this particular assembly. The second one, is an xml file, that describes what types to save, and you can even choose in those types which fields or methods you want to link.
So, when linking your assemblies, you have to know if you’re doing some dynamic loading. If yes, you have to tell the linker using a xml descriptor which dynamic types you want to save, otherwise, they may very well be not present in the output assemblies.
That’s a little bit painful, and that forces you to maintain a list of such types/assemblies. To ease your pain, I wrote something that I find really useful, even if it’s not completely done yet.
Guys, that was the biggest introduction ever to talk about a little piece of code. This little guy is a profiler for Mono, that is simply triggered every time the JIT will leave a method.
It’s goal is to produce an xml file that the linker understand, from a running application. For instance, before linking MoMa, I used:
# mono --profile=link:moma.xml ./MoMa.exe
Then I’ve simply used MoMa like I would if I was simply analysing an application to submit a report, and at the end, I had an interesting moma.xml file. When opening it, I’ve seen:
<linker>
<!-- ... -->
<assembly fullname="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756">
<type fullname="Mono.Posix.UnixEndPoint">
<method signature="System.Void .ctor(System.String)" />
</type>
<!-- ... -->
</assembly>
<!-- ... -->
</linker>
As we can see, during the execution, we’ve been using the constructor of the type Mono.Posix.UnixEndPoint in the assembly Mono.Posix. That’s an information the linker would not have been able to know. By using the profiler, one can prepare the linker to do its job with more data that it would have been able to gather by itself.And that’s pretty good to be able to do it without requiring me to write xml descriptors for every possible type that is dynamically loaded.
See, I can even write a few lines of C, isn’t that amazing?
Bat Monobile 1
These last days, I’ve been working on a little framework based both on Boo and NUnit called Bat. Bat stands for Boo Assembly Tester, and is attended to simplify the creation of unit tests for everything Cecil based.
For instance, let say I want to test the way Cecil emits assemblies, with a very simple Hello World assembly, here is the declaration of the test:
[Test]
public void CreateHelloWorld ()
{
RunWriteAssemblyTestCase ("HelloWorld");
}
And here is the actual test:
""" Hello, world! """ worker = Main.CilWorker worker.Emit(OpCodes.Ldstr, "Hello, world!") worker.Emit(OpCodes.Call, ImportConsoleWriteLine()) worker.Emit(OpCodes.Ret)
The method RunWriteAssemblyTestCase will compile the Boo script, run it, and thus create a new assembly, run the assembly, and ensure that the output match the documentation of the Boo script.
The framework provides helper to test the ability to read assemblies, or to round trip them, and ensure that they still work as expected. The good thing is that it should be pretty easy to create it’s own helpers on top of that to write tests for assembly linking, merging, or for whatever tool that manipulates Cecil assemblies.
If you’re interested in writing unit tests for Cecil, you can checkout at the bat project and the updated Mono.Cecil.Tests project. People working with Cecil, don’t hesitate to submit new unit tests now that it’s both fun and easy!
This work is eavily influenced by what my friend Rodrigo and I have done on our different projects that always end up manipulating assemblies :) Thank you Rodrigo!
