Kilka miesięcy temu Microsoft opublikował wersję Community Technology Preview (CTP) swojego projektu o nazwie kodowej Roslyn.

Czym jest Roslyn? Microsoft w skrócie określa swoje nowe dzieło jako „Compiler as a Service”. Wprowadziło to nieco zamieszania na niektórych serwisach newsowych, gdzie metodą głuchego telefonu pojawiły się informacje, że firma ma zamiar umieścić kompilator w chmurze Windows Azure. Oczywiście nie jest to prawda. :)

Tradycyjnie kompilatory są czarnymi skrzynkami, do których wrzucamy kod i dostajemy program w kodzie maszynowym bądź pośrednim. W czasie procesu kompilacji powstaje wiele produktów, takich jak drzewo składni czy tablica symboli. Po zakończonym procesie są one wyrzucane i użytkownik nie ma do nich dostępu mimo, że mogłyby się do czegoś przydać. Do czego konkretnie? Na przykład w celu stworzenia narzędzi do refactoringu. Obecnie większość produktów tego typu musi częściowo duplikować funkcjonalność kompilatora.

Microsoft rozwiązał ten problem przepisując kompilatory C# i VB na odpowiednio C# i VB :) oraz otwierając pipeline kompilacji.

Wersja CTP

Zainteresowanym praktycznym użyciem Roslyn, polecam pobranie wersji CTP, którą można znaleźć pod adresem http://msdn.microsoft.com/en-us/roslyn.

Do działania wymaga Visual Studio 2010 SP 1. Z góry ostrzegam, że projekt gryzie się nieco z Python Tools, co powoduje wyrzucanie komunikatu o błędzie przy każdym starcie VS (na szczęście nic poważniejszego).

Wykorzystanie Roslyn w Visual Studio

Najbardziej widocznym w samplach i dokumentacji, a zarazem tym, które mnie najbardziej interesuje zastosowaniem Roslyn jest refactoring. „Gołe” Visual Studio prezentuje się w tej dziedzinie miernie, zwłaszcza w porównaniu do darmowego środowiska Eclipse, które oferuje o wiele lepszą pracę z interfejsami, niezbędnymi w testowalnym kodzie.

Roslyn udostępnia dwa kluczowe elementy niezbędne do wykonywania zaawansowanych refactoringów:

  • Drzewo składni
  • Informacje semantyczne

Drzewo składni to po prostu reprezentacja dokumentu w formie drzewiastej. Można je porównać do drzewa DOM w HTML-u. Informacje semantyczne są niezbędne do szerszego zrozumienia analizowanego kodu np. jakie interfejsy implementuje klasa.

Myślę, że najlepiej będzie to dokładniej omówić na przykładzie. Posłużę się w tym celu znanym z Resharpera narzędziem „Go to implementation”, które napisałem w ramach eksperymentów z Roslyn.

Visual Studio w standardzie posiada tylko możliwość nawigacji do definicji – Go to definition (F12). Problem pojawia się w sytuacji, gdy pracujemy z interfejsami np. mamy do czynienia z następującym kodem:

ISpell spell = new Fireball();
string target = "Goblin";

spell.Cast(target);

Po kliknięciu na Cast i wybraniu odpowiedniej opcji z menu, chcemy dostać listę implementacji interfejsu i wybrać, do którego mamy zostać przeniesieni:

Od Visual Studio otrzymujemy obiekt reprezentujący dokument i punkt (w przestrzeni jednowymiarowej), w którym znajduje się kursor. Teraz musimy znaleźć węzeł drzewa składni, który zawiera w sobie kliknięty punkt. Następnie, używając informacji semantycznych, pobieramy symbol reprezentujący klikniętą metodę interfejsu. Dalej korzystając z informacji semantycznych, dobieramy się do symbolu samego interfejsu.

Mając symbol interfejsu, przeszukujemy rekurencyjnie globalną przestrzeń nazw w poszukiwaniu typów implementujących ów interfejs. Kiedy mamy już listę konkretnych typów, pobieramy z niej listę symboli metod o tej samej sygnaturze jak ta kliknięta na początku. Na sam koniec wracamy do drzewa składni i pobieramy punkty i nazwy dokumentów, w których znajdują się interesujące nas metody.

Listę znalezionych lokacji wyświetlamy użytkownikowi.

Kod omawianego pluginu można pobrać poniżej. Dokładne wyjaśnienie szczegółów implementacyjnych zostawiam na inną okazję.

http://dl.dropbox.com/u/6088895/RoslynOverwiew_code.7z

MEF

Poza możliwością wykorzystania w zwykłych Visual Studio Package’ach, Roslyn oferuje MEF-owe rozszerzenia. Pozwalają one na proste wpięcie się w mechanizmy edytora np. podkreślanie problemów i prezentowanie listy rozwiązań:

 

 

 

 

 

Na screenshocie powyżej widoczny jest efekt działania przykładu MakeConstCS z sampli Roslyn.

Zaimplementowanie tej funkcjonalności jest dość proste – wymaga utworzenia dwóch komponentów ICodeIssueProvidera i ICodeAction. Pierwszy z nich przeanalizuje kod i wskaże miejsca z wykrytymi problemami oraz proponowane rozwiązania.

     public interface ICodeIssueProvider
    {
        IEnumerable GetIssues(IDocument document, 
		CommonSyntaxNode node, 
		CancellationToken cancellationToken = null);
        IEnumerable GetIssues(IDocument document, 
		CommonSyntaxToken token, 
		CancellationToken cancellationToken = null);
        IEnumerable GetIssues(IDocument document, 
		CommonSyntaxTrivia trivia, 
		CancellationToken cancellationToken = null);
    }

Drugi naprawi problem.

    public interface ICodeAction
    {
        string Description { get; }
        ImageSource Icon { get; }
        ICodeActionEdit GetEdit(CancellationToken cancellationToken = null);
    }

ScriptEngine

Kolejną ciekawą funkcjonalnością dostarczaną przez Roslyn jest ScriptEngine. Pozwala on kompilować i wykonywać kod C# (albo VB) w czasie działania programu, podobnie jak ma to miejsce w wypadku języków skryptowych (np. IronPythona).

Zilustruję to na przykładzie prostego, kilkulinijkowego kalkulatora napisanego w WPF-ie.

Interfejs aplikacji składa się z TextBoxa, Buttona i TextBlocka.

 

Po kliknięciu na przycisk „równa się”, wykonany zostanie wykonany następujący kod:

ScriptEngine engine = new ScriptEngine(new [] {"System"}, new [] {"System"});

string expression = expressionTextBox.Text;

var result = engine.Execute(expression);
resultTextBlock.Text = result.ToString();

Przykładowy efekt działania widoczny jest poniżej.

 

Oczywiście możliwe jest przekazywanie do środka obiektów, na których będzie operował skrypt.

Podsumowanie

Roslyn nie trafi od razu do Visual Studio 11, niemniej mam nadzieję, że wersja finalna pojawi się w miarę szybko, bo to naprawdę świetne rozwiązanie. Znacząco ułatwia i tym samym obniża koszty tworzenia narzędzi działających na kodzie źródłowym, co pozwoli na tworzenie darmowych narzędzi przez niezależnych programistów.

Z ciekawostek warto dodać, że w przyszłości być może dostaniemy również możliwość podpinania się do innych elementów pipeline’u kompilatora, co rozszerzy możliwości metaprogramowania. Ja się nie mogę doczekać, bo dawno nie miałem w ręku tak fajnej zabawki :)