Skip to content

CLR, Metadata și IL

6 March 2008

Am să încerc să explic, pe scurt, cum funcționează CLR-ul (Common Language Runtime). Se știe că un assembly .NET (executabil sau bibliotecă) conține metadata și instrucțiuni IL – eu cel puțin am auzit asta la toate prezentările de .NET Framework la care am asistat – doar că nu se prea știe cu ce se mănâncă cele două.

Metadata e strâns legată de partea declarativă a aplicațiilor. Orice tip, metodă, variabilă etc. are o intrare în tabelul de metadata. Acestea pot fi descoperite folosind Reflection. De exemplu, clasa MethodInfo, reprezintă metadata asociată unei metode. Pentru a vedea metodele unei clase, de exemplu System.Console, folosind Reflection, avem:

using System;
using System.Reflection;
class Program
{
  public static void Main()
  {
    Type consoleType = Type.GetType("System.Console");

    foreach (MethodInfo methodInfo in ConsoleType.GetMethods())
    {
      Console.WriteLine(methodInfo.Name);
    }
  }
}

Metadata pentru o metodă are informații despre nume, atribute, parametri, return type și multe altele.

IL – Intermediate Language – sunt instrucțiunile executate de runtime. Setul de instrucțiuni este destul de mic (în jur de 200 de instrucțiuni) și lucrează cu o stivă. Orice instrucțiune consumă un număr de elemente de pe stivă și adaugă un număr de elemente pe stivă. Această caracteristică se numește delta-stack. În cazul în care un program încearcă să consume de pe stivă mai multe elemente decât există, sau lasă pe stivă elemente când aceasta ar trebui să fie goală, CLR-ul termină aplicația cu excepția InvalidProgramException. Unele instrucțiuni așteaptă și un metadata token. Un apel de funcție, de exemplu, așteaptă, după instrucțiunea call, token-ul funcției. Token-ul este de fapt adresa din tabela de metadata a funcției. Compilat, codul devine bytecode – instrucțiunile sunt reprezentate de intregi pe 8 sau 16 biți.

Să luăm de exemplu următoarea linie de cod C#:
Console.WriteLine("Hello World");

Tradus în limbaj de asamblare, linia arată astfel:
ldstr "Hello World"
call void [mscorlib]System.Console::WriteLine(string)

ldstr pune pe stivă string-ul ”Hello World”. call folosește token-ul pentru a ști ce funcție să apeleze – în cazul nostru WriteLine care așteaptă un string. WriteLine consumă string-ul de pe stivă și, la ieșire, lasă stiva goală.

Dacă vrem să adăugăm și un ReadLine după, avem
Console.WriteLine("Hello World");
Console.ReadLine();

În limbaj de asamblare, putem încerca:
ldstr "Hello World"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()

Dacă rulăm codul de mai sus ne vom trezi cu un InvalidProgramException. De ce? Păi ReadLine pune pe stivă un string, chiar dacă noi îl folosim doar ca să așteptăm un ENTER. String-ul respectiv rămâne acolo și dezechilibrează stiva. Ce trebuie făcut în astfel de situații? Folosim instrucțiunea pop. Pop ”aruncă” ultimul element de pe stivă.
ldstr "Hello World"
call void [mscorlib]System.Console::WriteLine(string)
call string [mscorlib]System.Console::ReadLine()
pop

O carte foarte bună despre acest subiect este Expert .NET 2.0 IL Assembler (eu lucrez cu .NET 2.0, nu am trecut încă la 3.5🙂 ). E clar că oricine are de gând să scrie un compilator având ca target .NET trebuie să înțeleagă ce este metadata, care sunt instrucțiunile IL și la ce să se aștepte de la ele.

O altă unealtă grozavă este ildasm, care vine cu .NET Framework. ildasm înseamnă IL Disassembler, cu care se pot dezasambla diferite programe și se pot vedea instrucțiunile generate de compilatoare.

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: