Çalışma-Zamanında Kod Derlemeye Var Mısınız?

   Serinin önceki makalelerinde Roslyn hakkında giriş düzeyi bilgi edinmiş, hatta ilk kodlamalarımıza başlayarak iki-üç satırla uygulamalarımızda nasıl scripting desteği sunabileceğimizi görmüştük. Bu makalemle yavaş yavaş daha eğlenceli kodlara geçmeyi planlıyorum; sanırım bir derleyici olarak Roslyn ile tanışmanın da zamanı geldi artık.

RoslynDerleyiciAPIveBoruHattı

   Önceki makalemden yukarıdaki grafiği hatırlayacaksınızdır. Derleyici boru hattı (pipeline) mevcut derleyicilerin iç yapısını göstermekteyken, Derleyici API’si ise Roslyn projesinin bizlere açmış olduğu API’leri ve derleyiciye müdahale edebileceğimiz noktaları göstermekte. Yazdığımız kaynak kodları daha önceden kara kutu olan derleyicilerde bu boru hattında işlem görmekte ve bize derleyici API’si sayesinde sürece müdahale edebilme/görme şansı verilmekte. Derleyici boru hattının diğer tarafına geldiğimizde ise artık elimizde IL kodlarına dönüştürülmüş assembly’ler olacaktır.

Roslyn

   Yukarıdaki grafiğin üst bölümünde yer alan Dil Hizmeti bizlere Roslyn ile sunulan API’lerden hangisinin hangi amaç ile kullanılabileceğini/kullanıldığını göstermektedir. Aslına bakarsanız Dil Hizmeti bölümündeki her bir girdi aynı zamanda Visual Studio içerisindeki bir özelliktir. Sizlere bu makalemde Derleyici API’sini kullanarak çalışma-zamanında bir kaynak kodu nasıl IL koduna dönüştürebileceğimizi anlatacağım. Bundan da öte, dinamik oluşturulan bu assembly’yi yine çalışma-zamanında nasıl uygulamanız içerisine yükleyerek kullanabileceğinizden bahsedeceğim.

   Yazılım dünyasını vazgeçilmez geleneğidir “Hello World” (Merhaba Dünya) örneklemesi, Roslyn ile birlikte gelen derleyici API’sini anlatırken sanırım verilebilecek en iyi giriş örneğide bu olacaktır;

using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;

//…

var kod = @"class MerhabaDunya { public static void Mesaj() { System.Console.WriteLine(""Enterprisecoding'den Roslyn'e Merhaba""); } }";
var assemblyAdi = "Enterprisecoding-" + Guid.NewGuid() + ".dll";

var sozDizinAgaci = SyntaxTree.ParseCompilationUnit(kod);
var derleme = Compilation.Create(
    assemblyAdi,
    options: new CompilationOptions(assemblyKind: AssemblyKind.DynamicallyLinkedLibrary),
    syntaxTrees: new[] { sozDizinAgaci },
    references: new[] { new AssemblyFileReference(typeof(object).Assembly.Location) });

Assembly derlenmisAssembly;
using (var akis = new MemoryStream()) {
    var derlemeSonucu = derleme.Emit(akis);
    derlenmisAssembly = Assembly.Load(akis.GetBuffer());
}

var merhabaDunyaTuru = derlenmisAssembly.GetType("MerhabaDunya");
var mesajMetodu = merhabaDunyaTuru.GetMethod("Mesaj");
mesajMetodu.Invoke(null, null);

Örnek konsol uygulamamızı çalıştırdığımızda "Enterprisecoding'den Roslyn'e Merhaba" yazısı görülecektir

   Örnek kodumuzu bir konsol projesinin main fonksiyonu içerisine koyacak olursanız çalıştırdığınızda yukarıdaki çıktıyı alacaksınız. String olarak derleyici API’sine verdiğimiz girdi derleyici tarafından yorumlanarak bize bir assembly akışı verilecektir. Assembly.Load fonksiyonu yardımıyla bu akışı yüklediğimizde artık içerisindeki türleri kullanabilir duruma geleceğiz.

   Şimdi isterseniz adım adım bu örneğimizi inceleyelim. Derlenecek kodu string olarak verip, oluşacak assembly adını belirledikten sonra öncelikle aşağıdaki satırla kodumuza ait söz dizim ağacını oluşturuyoruz;

var sozDizinAgaci = SyntaxTree.ParseCompilationUnit(kod);

   Makalemin birincil hedefinin string bir kod parçacığının derlenerek uygulama içerisinde kullanılması olduğu için söz dizim ağacı API’sinin detayına inmiyorum; bizlere önemli bilgiler sunduğunu söyleyebilirim. Bu API’yi merak edenlerin serinin sonraki makalelerini takip etmelerini tavsiye ederim.

   Söz dizim ağacı derleyici API’sinin temel girdisi olacaktır. Kodun devamında ise Compilation sınıfı yardımıyla bir derleme oluşturmaktayız.

var derleme = Compilation.Create(
    assemblyAdi,
    options: new CompilationOptions(assemblyKind: AssemblyKind.DynamicallyLinkedLibrary),
    syntaxTrees: new[] { sozDizinAgaci },
    references: new[] { new AssemblyFileReference(typeof(object).Assembly.Location) });

   Compilation sınıfı; derleyicinin C# ya da Visual Basic uygulama kodlarını derleyerek bir assembly oluşturabilmesi için ihtiyacı olan referanslar, derlenecek kaynak kodları, derleme seçenekleri gibi verileri bir arada tutmaktadır. Compilation içerisinde tutulan bu bilgiler sayesinde kaynak kod derleyici tarafından anlamlandırılabilir ve IL kodlarına dönüştürülebilir. Örneğimizde; verdiğimiz söz dizim ağacı kullanılarak bir dll oluşturulmasını ve bu dll oluşturulurken sadece mscorlib dll’ine referans verilmesini istediğimizi belirtmekteyiz.

   Compilation sınıfı yardımıyla derleyicinin çalışmasını istediğimiz bağlamı belirledikten sonra geriye sadece derleyiciye IL kodunu oluşturmasını söylemek kalıyor. Emit fonksiyonu yardımıyla yapılan bu işlemde dikkat edecek olursanız derleyici bize dosya sistemi üzerindeki bir dosyayı işaret etmek yerine bir akış sunmakta. Bu akış dosya sistemine yazılarak bir assembly oluşturulabileceği gibi örneğimizde olduğu gibi çalışma zamanından hiç dosya sistemine yazılmadan uygulamanın appdomain’ine yüklenebilir.

Assembly derlenmisAssembly;
using (var akis = new MemoryStream()) {
    var derlemeSonucu = derleme.Emit(akis);
    derlenmisAssembly = Assembly.Load(akis.GetBuffer());
}

   Bu işlemler ardında artık kaynak kodumuz çalışma-zamanında derlenerek hafızaya yüklenmiş olacaktır. Bu noktadan sonra aynı standart bir assembly içerisinde işlem yapar gibi işlemlerimizi gerçekleştirebiliriz. Örneğimizde reflection yardımıyla bu derlemede oluşan türlere ulaşmakta ve MerhabaDunya sınıfı içerisindeki Mesaj fonksiyonunu çalıştırmaktayız. Verilen bir kaynak kodun Derleyici API’si ile bir assembly’ye dönüştürülmesi aynı bu kadar kolay.

   Giriş seviyesindeki bir örnekle sizlere detaylarını vermeye çalıştığım Derleyici API’si bize yepyeni bir dünyanın kapılarını açmakta. Çalışma-Zamanında bir kaynak kodu derleyebilmek ve uygulamaya yükleyip kullanabilmek bize kullanıcı alışkanlıklarını öğrenerek kendini bu doğrultuda yeniden düzenleyen, kendi kendine gelişen uygulamalar yapabilme fırsatını sunacaktır.

  Derleyici API’sine giriş yapabilmek adına basit tutmaya çalıştığım bu makalem sonrasında takip eden makalemde sizlerle Derleyici API’si yardımıyla uygulamalarınızı nasıl daha iyi hale getirebileceğiniz dair güzel bir örnek paylaşmayı planlıyorum.

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir