Common Intermediate Language
Common Intermediate Language (CIL, vyslovováno jako „sil“, případně „kil“, dříve Microsoft Intermediate Language, MSIL) je v informatice nejnižší člověkem čitelný programovací jazyk definovaný specifikací Common Language Infrastructure používaný projekty .NET Framework a Mono. Jazyky, které se zaměřují na CLI kompatibilní prostředí, jsou sestavovány do bajtkódu. CIL patří mezi objektově orientované jazyky výhradně zásobníkového typu (anglicky stack-based). Prováděn je prostřednictvím virtuálního stroje.
CIL byl původně během uvolňování beta .NET jazyků znám jako Microsoft Intermediate Language (MSIL). Vzhledem ke standardizaci C♯ a Common Language Infrastructure je bajtkód nově a oficiálně označován jako CIL.
Základní informace
Při kompilování .NET programovacích jazyků je zdrojový kód přeložen do CIL kódu (nepoužívá se platformně nebo do výpočetně specifický objektový kód). CIL je procesorově a zároveň platformě nezávislý soubor instrukcí, které mohou být realizovány v jakémkoli prostředí podporujícím Common Language Infrastructure (může se jednat buď o .NET runtime pro operační systém Microsoft Windows, nebo samostatně odvozené Mono, které pracuje pod operačními systémy UNIXového typu). CIL kód je za běhu ověřován z hlediska bezpečnosti, a proto poskytuje lepší zabezpečení a spolehlivost, než nativně kompilované binární soubory.
Proces spuštění vypadá takto:
- Zdrojový kód je převeden do Common Intermediate Language, CLI ekvivalent k nižším programovacím jazykům pro CPU.
- CIL je pak převeden do bajtkódu a je vytvořeno .NET assembly.
- Po provedení .NET assembly, jeho bajtkód projde skrz provozní JIT kompilátor, aby generoval nativní kód.
- Nativní kód je zpracováván pomocí procesoru.
Instrukce
CIL bytecode má instrukce pro následující skupiny úloh:
- Načítání a ukládání
- Aritmetické
- Typ konverze
- Vytváření objektů a manipulaci s nimi
- Operand managementu zásobníku (push / pop)
- Kontrola převodu (větvení – Branching)
- Volání metod
- Vyvolání výjimky
- Synchronizace
Výpočetní model
Common Intermediate Language je považován za objektově orientovaný jazyk zásobníkového typu (stack-based). To znamená, že data jsou ukládána z registrů do zásobníku místo toho, aby byla odkládána do paměti jako ve většině CPU architektur.
V assembleru x86 architektury IA-32 by to mohlo vypadat následovně:
add eax, edx
mov ecx, eax
Odpovídající kód v CIL by mohl vypadat takto:
ldloc.0
ldloc.1
add
stloc.0 // a = a + b or a += b;
V programu jsou použity dvě lokálně proměnné, které jsou přesunuty do fronty. Když je přidaná instrukce zavolána, operand je ze zásobníku vyzvednut a výsledek je na něj obratem uložen. Hodnota je pak vyzvednuta a uložena jako první lokální proměnná.
Objektově-orientované koncepty
V objektově-orientovaném konceptu můžete vytvářet objekty, volat metody a používat další typy členů, jako jsou například pole. CIL je navržen jako objektově orientovaný a všechny metody (až na výjimky) musí být obsažené v příslušné třídě.
Příklad statické metody:
.class public Foo
{
.method public static int32 Add(int32, int32) cil managed
{
.maxstack 2
.locals init (
[0] int32 num1,
[1] int32 num2
)
ldloc.0
ldloc.1
add
stloc.0 // a = a + b or a += b;
ret // return a;
}
}
Použitá metoda nevyžaduje žádnou deklaraci instance třídy Foo, protože je statická. To znamená, že náleží do třídy a může být následně použita jako v tomto případě v C♯.
int r = Foo.Add(2, 3); //5
V CIL
ldc.i4.2
ldc.i4.3
call int32 Foo::Add(int32, int32)
stloc.0
Instance tříd
Instance třídy obsahuje nejméně jeden konstruktor a nějaké další členy. Tato třída má sadu metod reprezentujících akce objektu Car.
.class public Car
{
.method public specialname rtspecialname
instance void .ctor(int32, int32) cil managed
{
/* Constructor */
}
.method public void Move(int32) cil managed
{
/* Omitting implementation */
}
.method public void TurnRight() cil managed
{
/* Omitting implementation */
}
.method public void TurnLeft() cil managed
{
/* Omitting implementation */
}
.method public void Break() cil managed
{
/* Omitting implementation */
}
}
Vytváření objektů
Instance tříd jsou v C♯ vytvářeny následujícím způsobem:
Car myCar = new Car(1, 4);
Car yourCar = new Car(1, 3);
Příkazy jsou přibližně stejné jako tyto instrukce:
ldc.i4.1
ldc.i4.4
newobj instance void Car::.ctor(int, int)
stloc.0 // myCar = new Car(1, 4);
ldc.i4.1
ldc.i4.3
newobj instance void Car::.ctor(int, int)
stloc.1 // yourCar = new Car(1, 3);
Vyvolání metody instance
Například metody jsou volány následovně:
myCar.Move(3);
V CIL:
ldloc.0 // Load the object "myCar" on the stack
ldc.i4.3
call instance void Car::Move(int32)
Metadata
.NET zaznamenává informace o kompilovaných třídách jako metadata. Proces čtení těchto metadat se nazývá zrcadlení (reflection). Metadata mohou být data v podobě atributů. Ty mohou být volně rozšířeny o atributy třídy, čímž se stávají velmi silným nástrojem.
Příklad
Příklad je napsán v CIL a demonstruje kód „Hello, World“:
.assembly Hello {}
.assembly extern mscorlib {}
.method static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello, world!"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
Tento kód může být porovnán s odpovídajícím kódem Java bytecode:
static void Main(string[] args)
{
outer:
for (int i = 2; i < 1000; i++)
{
for (int j = 2; j < i; j++)
{
if (i % j == 0)
goto outer;
}
Console.WriteLine(i);
}
}
V CIL-syntaxi by zápis vypadal následovně:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (int32 V_0,
int32 V_1)
IL_0000: ldc.i4.2
stloc.0
br.s IL_001f
IL_0004: ldc.i4.2
stloc.1
br.s IL_0011
IL_0008: ldloc.0
ldloc.1
rem
brfalse.s IL_0000
ldloc.1
ldc.i4.1
add
stloc.1
IL_0011: ldloc.1
ldloc.0
blt.s IL_0008
ldloc.0
call void [mscorlib]System.Console::WriteLine(int32)
ldloc.0
ldc.i4.1
add
stloc.0
IL_001f: ldloc.0
ldc.i4 0x3e8
blt.s IL_0004
ret
}
Výše je správná reprezentace toho, jak vypadá CIL blízko VM úrovně. Když jsou zkompilované metody uloženy v tabulkách a instrukce zase v bytech uvnitř assembly, který je přenosně spustitelným souborem (Portable Executable-file).
Vývoj
CIL assembly a instrukce jsou generovány buď kompilátorem, nebo nástrojem zvaným IL Assembler (ILASM). Složené IL může být také znovu rozloženo do kódu – užitím IL Disassembler (ILDASM). Podobně, jako v případě Java bytecode, existují též nástroje, které nabízejí dekompilaci IL do jazyků vyšší úrovně (např. C♯, Visual Basic .NET). Za jejich vlajkovou loď lze považovat aplikaci .NET Reflector. Tyto nástroje však mají protiváhy, jež usilují o znepřehlednění kódu při zachování plné funkcionality tak, aby dekompilace do jazyků vyšší úrovně selhala.
Kompilace just-in-time
Kompilace Just in Time (JIT) překládá bajtkód do kódu vykonatelného přímo mikroprocesorem, tj. do tzv. „nativního kódu“. Tento překlad je prováděn během provádění programu. JIT kompilace poskytuje optimalizace specifické pro prostředí, typovou bezpečnost kontrolovanou za běhu a verifikaci. K dosažení tohoto kompilátor testuje metadata nashromážděná během překladu na nepovolené přístupy do paměti.
Reference
V tomto článku byl použit překlad textu z článku Common Intermediate Language na anglické Wikipedii.