Common Intermediate Language

Z Multimediaexpo.cz

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 byte kódu (Bytecode). 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 byte-kód nově a oficiálně označován jako CIL.

Obsah

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í 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 jako Linux či Unix). 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:

  1. Zdrojový kód je převeden do Common Intermediate Language, CLI ekvivalent k nižším programovacím jazykům pro CPU.
  2. CIL je pak převeden do byte-kódu a je vytvořeno .NET assembly.
  3. Po provedení .NET assembly, jeho byte kód projde skrz provozní JIT kompilátor, aby generoval nativní kód.
  4. 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
  • Vyhazová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 architektuře x86 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);
<source lang="csharp">
 
Příkazy jsou přibližně stejné jako tyto instrukce:
 
<source lang="cil">
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 jako IL Assembler (ILASM). Složené IL může být také znovu rozloženo do kódu užitím IL Disassembler (ILDASM). Existují další nástroje jako .NET Reflektor, které umožní dekompilaci IL do jazyků vyšší úrovně (např. C#, Visual Basic). Tato vlastnost je sdílená s Java bytecode. Ale existují nástroje, které mohou zmást kód, a to tak, že vlastní kód nemůže být rozložen, ale přesto je spustitelný.

Kompilace just-in-time

Kompilace just-in-time kompilace zahrnuje vrácení byte-kódu do kódu, který je okamžitě vykonatelný pomocí CPU. Konverze je prováděna postupně v průběhu vykonávání programu. JIT kompilace nabízí specifické optimalizace daného prostředí a ověření assembly. K dosažení tohoto, JIT kompilátor zkoumá nashromážděná metadata pro nezákonné přístupy a ovládá přiměřeně rušení.

Externí odkazy