Jun 21, 2016

To infinity and beyond : more powerful .NET Metadata querying with LINQPad custom drivers and Mono.Cecil

In my last post I discussed how we can use LINQPad to query .NET metadata using .NET reflection. 

Even though that alone was already quite useful it has its own drawbacks:
  • No nice integration with LINQPad, requiring the user to load the assembly manually (even worse, forcing users to type the full assembly path)
  • No way to inspect / dump method bodies (so it is not possible, for instance, to find all methods that call into an specific method)
In order to overcome those limitations I decided to write a custom LINQPad driver for .NET assemblies (as we saw before this is not strictly necessary to use LINQ to explore .NET metadata, but it definitely can help to make the experience more pleasant/enabling some scenarios by, for instance, exposing some predefined Sources like all public typesmembers and the like).

You can read more about custom LINQPad drivers here, but in summary I created a static driver that exposes the following predefined query sources:
  • Types : A collection of all types (irrespective to accessibility and/or whether the type in question is a top level / inner type) from the assembly's main module.
  • PublicTypesA collection of all public types from the assembly's main module.
In addition to that I've also introduced some convenience functions that can be used in the queries:
  • AsString(method): An extension method for MethodBody that converts a method body into a formatted string with it's IL instructions.
  • Calls(method): An extension method for a MethodDefinition / MethodBody that takes a method (either as a MethodInfo or a MethodReference) and returns true if the method in question is being called in the method body.
  • References(type)An extension method for a MethodDefinition / MethodBody that takes a type (either as a string, a TypeInfo or a TypeReference) and returns true if the type in question is being referenced in the method body.
  • Property(name): An extension method (for TypeDefinition) that returns a property definition if the type has a matching one or null otherwise.
  • Event(name): An extension method (for TypeDefinition) that returns an event definition if the type has a matching one or null otherwise.
  • Field(name): An extension method (for TypeDefinition) that returns a field definition if the type has  a matching one or null otherwise.
You can have a glimpse of those features in the following screenshot:


If you are interested in using this, all you need to do is to download Cecil.LINQPad.Driver.lpx and install it in LINQPad by clicking Add connection "link" then "View more drivers...", then "Browse" and finally selecting Cecil.LINQPad.Driver.lpx file

Of course you can also grab the source code from GitHub and build it for yourself ;)

Feel free to comment, make suggestions, report issues, ask questions, etc.

Have fun!

Para o infinito e além: consultas ainda mais poderosas sob metadados NET com drivers parta LINQPad e Mono.Cecil

No post anterior mostrei como usar o LINQPad para executar consultas em metadados .NET usando .NET Reflection, técnica que, apesar de muito útil, possui algumas limitações:
  • Zero integração com LINQPad, exigindo que o assembly seja carregado manualmente (ainda pior é fato de termos que digitar o caminho completo do assembly),
  • Como o .Net Reflection não expõe o corpo (instruções) de métodos, não é possível por exemplo, encontrar todos os métodos que executam algum outro método específico ou que acessem uma propriedade, etc.
Felizmente o LINQPad nos permite desenvolver / utilizar custom drivers que se integram muito mais naturalmente com o mesmo. No meu caso eu escrevi um que usa o Mono.Cecil (ao invés de .Net Reflection) para expor algumas fontes pré-definidas para consultas:
  • Types : Uma lista com todos os tipos (independentemente do tipo ser público ou não e/ou ser uma inner class) definidos no módulo principal do assembly.
  • PublicTypes: Uma lista composta por todos os tipos definidos publicamente.
Também introduzi as seguintes extensões (extension methods):
  • AsString(): Método que estende MethodBody retornando as instruções que compõe o corpo do método na forma de uma string.
  • Calls(method): Método que estende MethodDefinition / MethodBody retornando verdadeiro (true) caso o método passado como parâmetro seja executado pelo corpo do método estendido.
  • References(type)Método que estende MethodDefinition / MethodBody retornando verdadeiro (true) se o tipo passado como parâmetro for referenciado pelas instruções do corpo do método estendido.
  • Property(name): Método que estende TypeDefinition retornando a propriedade especificada pelo parâmetro (caso o tipo possua uma propriedade com o mesmo nome) ou null.
  • Event(name): Método que estende TypeDefinition retornando o evento que possua o nome igual ao passado como parâmetro ou null caso o tipo em questão não possua um evento com este nome.
  • Field(name): Método que estende TypeDefinition retornando o campo que possua o nome igual ao especificado no parâmetro ou null caso o tipo em questão não possua um campo com este nome.
Você pode ver um exemplo da utilização de tais métodos na imagem abaixo:


Para usar este Custom Driver você pode baixar o arquivo Cecil.LINQPad.Driver.lpx e instalar o mesmo no LINQPad clicando na opção Add connection,  depois em "View more drivers...", "Browse" e finalmente selecionando o arquivo que você acabou de baixar (Cecil.LINQPad.Driver.lpx)

É claro que você também pode baixar o source do mesmo do GitHub e compilar / instalar ;)


Uma vez instalado, o mesmo aparecerá na lista de opções de drivers disponíveis:



Pressione o botão Next e, a seguir selecione o assembly que você deseja fazer consultas. Lembre-se de, após abrir o assembly, clicar no dropdown "Connection" para selecionar a conexão que o LINQPad deve usar para rodar as consultas, ou seja, qual assembly deve ser usado como fonte nas consultas. No exemplo abaixo o assembly Boo.Lang.Parser.dll será usado:

Pronto. Agora você já pode rodar suas consultas LINQ!

Have fun!

Jun 16, 2016

Realizando consultas em assemblys .NET

Desde 2008 venho não apenas desenvolvendo soluções em NET como também  tenho dedicado um tempo considerável manipulando assemblys .NET o que, invariavelmente, me leva a frequentemente ter que responder questões como:
  • Quantos tipos em um assembly específico são Value Types?
  • Quantos (e quais) tipos expões mais que x propriedades ?
  • Existe alguma interface / classe que não segue alguma convenção de nomenclatura?etc.
Para responder estas questões eu basicamente usava duas possíveis estratégias: i) escrevia um programa usando Mono.Cecil (ou .NET Reflection) e assim navegava nos metadados (métodos, tipos, etc) ou ii) recorria a mágica das regular expressions (o que sempre me garantia algumas horas de "diversão"). 

O problema com estas soluções não é que as mesmas não funcionem, mas sim que frequentemente eu acabava realizando manualmente, tarefas tecnicamente passiveis de ser automatizadas.

Mas esta situação estava destinada a mudar desde minha última viagem para um evento interno na empresa em que trabalho. Neste evento eu participei em um projeto onde alguns amigos usaram o LINQPad para visualizar o MS IL gerado para um código C# específico. Normalmente eu usaria o ILSpy para isto mas, novamente, para tanto eu teria que escrever um programa e compilar o mesmo para, só depois, abrir o assembly no ILSpy o que me fez olhar com mais atenção para o LINQPad e, finalmente, cair a ficha: eu poderia usar o LINQPad não apenas para visualizar o IL gerado rapidamente; mais importante que isso, eu poderia usar o mesmo para automatizar minhas "pesquisas" em assemblys .NET! (nossa, descobri a pólvora :)

Assim que retomei minhas tarefas diárias eu comecei a testar a ideia de fazer consultas sobre .NET assemblys e o resultado me deixou bem satisfeito. Sinceramente, a ideia é tão simples que eu não entendo porque levou tanto tempo para eu pensar nisso :(. O exemplo abaixo demonstra o uso da técnica:

var a = Assembly.LoadFrom(@"S:\Unity\Editor\Data\Managed\UnityEngine.dll");
var result = from t in a.GetTypes()
             where Char.IsLower(t.Name[0])
             select t.FullName;

result.Dump();

Não consigo imaginar uma forma muito mais simples para você obter uma lista de tipos em um assembly cujo nome comece com uma letra minúscula! (tente fazer isso usando regular expressions nos fontes de um projeto :) O melhor é que este trecho de código é uma consulta LINQ padrão sobre metadados .NET, ou seja, nada de novo :)

Apesar de não haver quase nada específico (apenas o método Dump()) ao LINQPad na consulta acima, o uso deste aplicativo torna o processo muito mais simples e natural: nada de escrever um programa para rodar este trecho de código; nada de compilar, etc.

Se você deseja experimentar também, basta copiar/colar a consulta acima no LINQPad, modificar o path do assembly para um que faça sentido para você e executar a mesma pressionando F5:




Simples assim! É claro que para utilizar esta técnica eficientemente você deve ter um bom conhecimento sobre .NET Reflection mas isto não deve ser problema.

No próximo post eu descreverei como tornar estas consultas ainda mais flexíveis através da introdução de um custom driver para o LINQPad!

Have fun!

Read this post in English.

Querying .NET assemblies with LINQPad

Since 2008 I have not only been playing / working with .NET on a daily basis; I've also done quite a lot of work with .NET assemblies and, as result, quite frequently I find myself trying to answer questions like:
  • How many types in the assembly are value types?
  • How many types exposes more than x properties?
  • Do we have any interfaces / types that does not conform to some specific naming convention?
  • etc.
In order to answer these questions I used to either write a quick program (with the help of Mono.Cecil) or ended up having some fun time with regular expressions (of course, running these regular expressions on the source code instead of the built assemblies). These solutions work relatively well but they have some serious drawbacks: i) lots of repetitive work, ii) it is harder to extract information from the source than from assemblies; iii) some times I don't have any source code at all, only the assembly, etc.

Since last Hackweek this was set to change! For some reason I can't explain (it is as if I have been in Mars for the last couple of years ;) I failed to even realize how powerful / flexible LINQPad has become; it is not the case that I was not aware about the tool itself; I knew LINQPad for some time (lets say, for 6 ~ 7 years) but I've never used it util last May; at that time a bunch of team mates were using it to quickly check generated IL for some C# code snippet and since I was participating in the same project I also needed to the same, so I decided to give it a try.

After returning to my daily tasks I sat one day and started experimenting with the idea of running queries on .NET assemblies for a couple of hours, and ended up really happy with the outcome. Actually, it was so easy that I have no idea why it took me so long to start using it: basically we can use .NET reflection and LINQ for Objects (as you can see in the following example):

var a = Assembly.LoadFrom(@"S:\Unity\Editor\Data\Managed\UnityEngine.dll");
var result = from t in a.GetTypes()
             where Char.IsLower(t.Name[0])
             select t.FullName;

result.Dump();

or, in other words, show me all the types with a name starting with a lowercase char!

That is it, simple like that. The best part is that it is plain C# using LINQ and reflection to navigate through the .NET metadata.

Of course there's (almost) nothing specific to LINQPad in that code snippet; to me, the value that LINQPad aggregates lies on how easy and natural it looks like to use it to type such queries without the need to write / compile a separate application and the quick iteration times it enables.

If you want to try it also just copy / past that code to LINQPad, change the assembly path to one that makes sense to you and run (by pressing F5):




Once we have a way to easily run standard LINQ queries over assemblies we can unleash our imagination and rely on LINQPad power. But, before you get too excited, this approach does have one caveat though: you'll need to have a good grasp about .NET reflection to be able to use it effectively.

In my next post I'll write a custom LINQPad Driver in order to use Mono.Cecil instead of .NET Reflection making it even easier / more flexible to use!

Have fun!

Leia este post em Português.