Common Intermediate Language

A Common Intermediate Language (CIL)a legalacsonyabb szintű, ember által olvasható programozási nyelv a .NET Keretrendszerben és a Common Language Infrastructure-ben. Azok a nyelvek, melyek a .NET keretrendszert vagy a Monot használják, a CIL-re fordítanak, ami aztán kezelt kódra fordul. A CIL egy teljesen objektumorientált és verem-alapú assembly nyelv, melyet egy futásidejű környezet hajt végre. Az elsődleges .NET nyelvek a C#, a Visual Basic .NET és a Managed C++. Bájtkódja tovább fordítható natív kódra is.

A .NET nyelvek béta kiadásakor a CIL-t még Microsoft Intermediate Language-nek (MSIL) hívták. A szabványosításnak köszönhetően a kezelt kód neve hivatalosan CIL.

A CIL egy másik jelentésében C Intermediate Language-re utal, ami a C egy egyszerűsített változata, amire a kódot további elemzés céljára használják.

Bővebben 
Az Assemblyvel szemben a CIL független a processzortól és az operációs rendszertől. Bármely környezetben végrehajtható, ami támogatja a Common Language Infrastructure-t, mint a .NET runtime Windowson, vagy a több platformos Mono futtatórendszer. Ez elméletben szükségtelenné teszi a különböző változatok terjesztését különböző CPU-kra és operációs rendszerekre.

A végrehajtás folyamata a következő:
 A forráskódot CIL-re konvertálják, ami a CLI assemblyje.
 A CIL bájtkódra fordul, és CLI Assemblyt hoznak belőle tére.
 Ennek végrehajtása közben a futtatókörnyezet JIT fordítója natív kódot generál. Ezt a lépést előzetesen is el lehet végezni, de ezzel elveszítjük a hordozhatóságot.
 A processzor végrehajtja a natív kódot.

Utasításkészlet 
A CIL bájtkódnak ezekre vannak utasításai:

 Betöltés és tárolás
 Aritmetika
 Típuskonverzió
 Objektumok készítése és kezelése
 Operandusok veremkezelése
 Vezérlésátadás
 Metódushívás és visszatérés
 Kivételdobás
 Monitor alapú párhuzamosság
 Pointerkezelés a C++/CLI és a nem biztonságos C# számára

Számítási modell 
A Common Intermediate Language objektumorientált és verem alapú. Ez azt jelenti, hogy regiszterek helyett az adatok verembe kerülnek, ami különbözik a legtöbb CPU architektúra Assemblyjétől.

Egy összeadás az x86 Assemblyjétől:
add eax, edx

A megfelelő kód az IL nyelvben:
ldloc.0
ldloc.1
add
stloc.0    // a = a + b or a += b;

Itt két local változó kerül a verembe. Add utasítás hívásakor az operandusok kikerülnek a veremből, majd az eredmény vissza. A visszamaradt érték az első localba kerül.

Objektumorientáltság 
Az objektumorientáltság lehetővé teszi osztályok definiálását mezőkkel és metódusokkal együtt.

A CIL-t objektumorientáltnak tervezték, és minden metódusnak osztályban kell lennie. Így például ennek a static metódusnak is:

.class public Foo
{
    .method public static int32 Add(int32, int32) cil managed
    {
        .maxstack 2
        ldarg.0 // load the first argument;
        ldarg.1 // load the second argument;
        add     // add them;
        ret     // return the result;
    }
}

A static azt jelenti, hogy a metódus nem példányhoz, hanem osztályhoz tartozik. Úgy használható, mint C#-ban:
int r = Foo.Add(2, 3);    // 5

In CIL:
ldc.i4.2
ldc.i4.3
call int32 Foo::Add(int32, int32)
stloc.0

Példányosztályok 
A példányosztályok tartalmaznak legalább egy konstruktort és példány adattagokat. Tartalmazhatnak mindezekből static elemeket, de nem lehet minden elemük static. Az alábbi példa egy autók mozgását modellező osztály váza.

.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 Brake() cil managed
    {
        /* Omitting implementation */
    }
}

Objektumok létrehozása 
C#-ban az osztályok így példányosíthatók:
Car myCar = new Car(1, 4); 
Car yourCar = new Car(1, 3); 

Ez hasonlóan működik a CIL-ben:
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);

Metódusok hívása 
Metódusok hívása a C#-ban:
myCar.Move(3);

A CIL-ben:
ldloc.0    // Load the object "myCar" on the stack
ldc.i4.3
call instance void Car::Move(int32)

Metaadatok 
A CLI metaadatokban tárol információt az osztályokról. Ahogy a típuskönyvtár a Component Object Modelben, ez lehetővé teszi, hogy az alkalmazások felderítsék és felhasználják az interfészeket, osztályokat, típusokat, metódusokat és mezőket. A metaadatok olvasását reflexió,

A metaadatok attributumok formájában tárolhatók. Az atrributumok az Attribute osztályból származnak, ami sok hasznos eszközt ad. Lehetővé teszi, hogy az osztályt alkotója extra információval lássa el, ami az osztály felhasználói számára sokféleképpen lehet hasznos az alkalmazás módjától, témájától függően.

Pointer utasítások 
A Java bájtkódjával szemben a CIL biztosítja az ldind, stind, ldloca utasításokat, és más utasításokat is az adat- és függvénypointerek kezelésére. Ez inkább a C++ kód menedzseléséhez hasznos, de nem biztonságos környezetben a C# nyelvben is lehet pointereket használni.

class A {
   public: virtual void __stdcall meth() {}
};
void test_pointer_operations(int param) {
	int k = 0;
	int * ptr = &k;
	*ptr = 1;
	ptr = &param;
	*ptr = 2;
	A a;
	A * ptra = &a;
	ptra->meth();
}

A megfelelő CIL kód:

.method assembly static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) 
        test_pointer_operations(int32 param) cil managed
{
  .vtentry 1 : 1
  // Code size       44 (0x2c)
  .maxstack  2
  .locals ([0] int32* ptr,
           [1] valuetype A* V_1,
           [2] valuetype A* a,
           [3] int32 k)
// k = 0;
  IL_0000:  ldc.i4.0 
  IL_0001:  stloc.3
// ptr = &k;
  IL_0002:  ldloca.s   k // load local's address instruction
  IL_0004:  stloc.0
// *ptr = 1;
  IL_0005:  ldloc.0
  IL_0006:  ldc.i4.1
  IL_0007:  stind.i4 // indirection instruction
// ptr = &param
  IL_0008:  ldarga.s   param // load parameter's address instruction
  IL_000a:  stloc.0
// *ptr = 2
  IL_000b:  ldloc.0
  IL_000c:  ldc.i4.2
  IL_000d:  stind.i4
// a = new A;
   IL_000e:  ldloca.s   a
  IL_0010:  call       valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'A.{ctor}'(valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst))
  IL_0015:  pop
// ptra = &a;
  IL_0016:  ldloca.s   a
  IL_0018:  stloc.1
// ptra->meth();
  IL_0019:  ldloc.1
  IL_001a:  dup
  IL_001b:  ldind.i4 // reading the VMT for virtual call
  IL_001c:  ldind.i4
  IL_001d:  calli      unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)(native int)
  IL_0022:  ret
} // end of method 'Global Functions'::test_pointer_operations

Példa 
A "Hello, world!" program C# nyelven:
 class HelloWorldApp
 {
    static void Main()
    {
        System.Console.WriteLine("Hello world.");
    }
 }
Ugyanez CIL nyelven:
 .assembly HelloWorld
 .class auto ansi HelloWorldApp
 {
     .method public hidebysig static void Main() cil managed
     {
          .entrypoint
          .maxstack 1
          ldstr "Hello world."
          call void [mscorlib]System.Console::WriteLine(string)
          ret
     }
 }

CIL szintaxisban:
.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack  2
    .locals init (int32 V_0,
                  int32 V_1)

              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_001b
              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)
    IL_001b:  ldloc.0
              ldc.i4.1
              add
              stloc.0
    IL_001f:  ldloc.0
              ldc.i4     0x3e8
              blt.s      IL_0004
              ret
}

Ez azt mutatja, hogyan működik a CIL VM-közeli nyelven. Fordításkor a metódusokat táblákban tárolja, és az utasításokat bájtokban az assemblyn belül, ez a Portable Executable forma.

Generálás 
A CIL assemblyt és utasításokat vagy a fordító, vagy az IL Assembler (ILAsm) alkalmazás generálja, ami a végrehajtási környezettel jön.

A CIL kód visszafordítható az IL Disassemblerrel (ILDASM). Vannak további eszközök, mint a .NET Reflector, ami CIL-ből magasabb szintű nyelvre fordít, mint C# vagy Visual Basic. Emiatt a CIL könnyen visszafejthető, hasonlóan a Java bájtkódhoz. Vannak eszközök, amelyekkel a forrás nehezen olvashatóvá tehető, amiből ugyanaz fordul, mint az eredeti.

Végrehajtás 
A futásidejű fordítás (JIT) a bájtkódot közvetlenül a CPU számára végrehajthatóvá fordítható. A konverziót szakaszonként hajtja végre a végrehajtás alatt. Eközben nyújt környezetspecifikus optimalizációt, futásidejű típusbiztonságot, és assembly verifikációt is nyújt. Ehhez a JIT megvizsgálja a metaadatokat, hogy van-e illegális hozzáférés, és ennek megsértését megfelelően kezeli.

A CPU-kompatibilis végrehajtási környezet lehetőséget ad az előzetes fordításra is (AOT). Ez lehetővé teszi a futás felgyorsítását. Ezt .NET Frameworkben a Native Image Generator (NGEN) hajtja végre, és a Mono is képes rá.

Fordítás

Jegyzetek 

Programozási nyelvek