Common Intermediate Language

De la Wikipedia, enciclopedia liberă

Common Intermediate Language (CIL), cunoscută anterior sub denumirea Microsoft Intermediate Language (MSIL), este limbajul de programare cel mai scăzut la nivel de limbaj uman, definit de specificațiile infrastructurii lingvistice comune (CLI), și este utilizat de .NET Framework, .NET Core și Mono. Limbile care vizează un mediu de rulare compatibil cu CLI se compilesază la CIL, care este asamblat într-un cod obiect care are un format în stil octet. CIL este un limbaj de asamblare orientat pe obiect și este în întregime bazat pe stiva. Bytecode-ul său este tradus în cod nativ sau cel mai frecvent executat de o mașină virtuală.

CIL a fost inițial cunoscut sub numele de Microsoft Intermediate Language (MSIL) în timpul lansărilor beta ale limbajelor .NET. Datorită standardizării C# și a infrastructurii lingvistice comune, bytecode-ul este acum cunoscut oficial ca CIL[1].

Modelul computațional[modificare | modificare sursă]

Common Intermediate Language este orientată pe obiecte și bazată pe stiva. Asta înseamnă că datele sunt împinse pe un teanc în loc să fie scoase din registre ca în majoritatea arhitecturilor procesorului.

În x86 ar putea arăta astfel:

add eax, edx

Codul corespunzător din IL poate fi redat ca acesta, unde 0 este eax și 1 este edx:

ldloc.0    // push local variable 0 onto stack
ldloc.1    // push local variable 1 onto stack
add        // pop and add the top two stack items then push the result onto the stack
stloc.0    // pop and store the top stack item to local variable 0

Iată doi loc care sunt împinși pe stivă. Atunci când instrucțiunea de adăugare este denumită operanzii sunt afișați și rezultatul este împins. Valoarea rămasă este apoi afișată și stocată în primul loc.

Obiective orientate[modificare | modificare sursă]

Aceasta se extinde și la conceptele orientate pe obiect. Aveți posibilitatea să creați obiecte, să apelați metode și să utilizați alte tipuri de membri, cum ar fi câmpurile.

CIL este proiectat să fie orientat pe obiecte și fiecare metodă are nevoie (cu unele excepții) să locuiască într-o clasă. De asemenea, această metodă statică:

.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;
    }
}

Această metodă nu necesită declararea unei instanțe a Foo deoarece este statică. Aceasta înseamnă că aparține clasei și poate fi folosit ca acesta în C#:

int r = Foo.Add(2, 3);    // 5

În CIL:

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

Clase de instanță[modificare | modificare sursă]

O clasă de instanță conține cel puțin un constructor și un membru al instanței. Această clasă are un set de metode care reprezintă acțiunile unui obiect auto.

.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 */
    }
}

Crearea obiectelor[modificare | modificare sursă]

În instanțele de clasă C# sunt create astfel:

Car myCar = new Car(1, 4); 
Car yourCar = new Car(1, 3);

Aceste afirmații sunt aproximativ aceleași cu aceste instrucțiuni:

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);

Invocând metodele instanței[modificare | modificare sursă]

Metode de instanță sunt invocate ca cea care urmează:

myCar.Move(3);

În CIL:

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

Exemplu[modificare | modificare sursă]

Mai jos este un program de bază Program Hello, world! scris în CIL. Acesta va afișa șirul "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
}

Următorul cod este mai complex în numărul de opcode.

static void Main(string[] args)
{
    for (int i = 2; i < 1000; i++)
    {
        for (int j = 2; j < i; j++)
        {
             if (i % j == 0)
                 goto outer;
        }
        Console.WriteLine(i);
        outer:;
    }
}

În sintaxa CIL se arată astfel:

.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
}

Aceasta este doar o reprezentare a modului în care arată CIL aproape de nivelul VM. Când sunt compilate, metodele sunt stocate în tabele și instrucțiunile sunt stocate ca octeți în interiorul ansamblului, care este un executabil portabil (PE).

Generație[modificare | modificare sursă]

Un ansamblu CIL și instrucțiuni sunt generate fie de un compilator, fie de un utilitar numit IL Assembler (ILAsm) care este livrat împreună cu mediul de execuție.

CIL-ul asamblat poate fi de asemenea dezasamblat în cod din nou folosind IL Disasembler (ILDASM). Există și alte instrumente, cum ar fi .NET Reflector, care pot decompila CIL într-un limbaj de nivel înalt (de ex., C # sau Visual Basic). Acest lucru face CIL o țintă foarte ușoară pentru inginerie inversă. Această trăsătură este partajată cu Java bytecode. Cu toate acestea, există instrumente care pot ucide codul și îl pot face astfel încât codul să nu poată fi ușor de citit, dar încă să poată fi rulat.

Execuție[modificare | modificare sursă]

Compilație exactă în timp[modificare | modificare sursă]

Compilația Just-in-Time (JIT) implică transformarea codului de byte în cod imediat executabil de către procesor. Conversia se realizează treptat în timpul execuției programului. Compilația JIT oferă optimizarea specifică mediului, siguranța tipului de execuție și verificarea asamblării. Pentru a realiza acest lucru, compilatorul JIT examinează metadatele de asamblare pentru orice acces ilegal și tratează încălcările adecvate.

Compilație în timp real[modificare | modificare sursă]

Intervalele de execuție compatibile cu CLI au, de asemenea, opțiunea de a efectua o compilare Ahot-of-time (AOT) a unui ansamblu pentru a face executarea mai rapidă prin eliminarea procesului JIT în timpul rulării.

În .NET Framework există un instrument special numit Generatorul de imagini nativ (NGEN) care efectuează AOT. În Mono există, de asemenea, o opțiune de a face un AOT.

Instrucțiuni pentru pointer - C++/CLI[modificare | modificare sursă]

O diferență notabilă față de octetul Java este că CIL vine cu ldind, stind, ldloca și numeroase instrucțiuni de apel care sunt suficiente pentru manipularea indicilor de date/funcții necesare pentru a compila codul C/C++ în CIL.

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();
}

Codul corespunzător din CIL poate fi redat astfel:

.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

Note[modificare | modificare sursă]

  1. ^ „What is Intermediate Language(IL)/MSIL/CIL in .NET”. Accesat în . CIL: ... When we compile [a] .NET project, it [is] not directly converted to binary code but to the intermediate language. When a project is run, every language of .NET programming is converted into binary code into CIL. Only some part of CIL that is required at run time is converted into binary code. DLL and EXE of .NET are also in CIL form.