Společný jazyk pro středně pokročilé - Common Intermediate Language

Common Intermediate Language ( CIL ), dříve nazývaný Microsoft Intermediate Language ( MSIL ) nebo Intermediate Language ( IL ), je sada binárních instrukcí pro středně pokročilé jazyky definovaná ve specifikaci Common Language Infrastructure (CLI). Pokyny CIL jsou spouštěny runtime prostředím kompatibilním s CLI, jako je Common Language Runtime . Jazyky, které cílí na CLI, se kompilují na CIL. CIL je objektově orientované , zásobník na bázi bytový kód . Runtime obvykle kompiluje instrukce CIL v nativním čase do nativního kódu .

CIL byl původně známý jako Microsoft Intermediate Language (MSIL) během beta verzí jazyků .NET. Kvůli standardizaci C # a CLI je bytecode nyní oficiálně známý jako CIL. Definice virů v programu Windows Defender nadále odkazují na binární soubory kompilované s tímto názvem jako MSIL.

Obecná informace

Během kompilace programovacích jazyků CLI je zdrojový kód přeložen do kódu CIL, nikoli do objektového kódu specifického pro platformu nebo procesor . CIL je instrukční sada nezávislá na CPU a platformě, kterou lze provádět v jakémkoli prostředí podporujícím infrastrukturu Common Language, jako je .NET runtime ve Windows nebo cross-platform Mono runtime. Teoreticky to eliminuje potřebu distribuovat různé spustitelné soubory pro různé platformy a typy CPU. CIL kód je ověřen kvůli bezpečnosti za běhu, což poskytuje lepší zabezpečení a spolehlivost než nativně kompilované spustitelné soubory.

Proces provedení vypadá takto:

  1. Zdrojový kód se převede na byte kód CIL a vytvoří se sestavení CLI .
  2. Po provedení sestavení CIL je jeho kód předán kompilátorem JIT modulu runtime za účelem vygenerování nativního kódu. Lze také použít předběžnou kompilaci, která tento krok eliminuje, ale za cenu přenositelnosti spustitelného souboru.
  3. Procesor počítače provede nativní kód.

Instrukce

Bajtový kód CIL obsahuje pokyny pro následující skupiny úkolů:

Výpočtový model

Common Intermediate Language je objektově orientovaný a založený na zásobníku , což znamená, že parametry a výsledky instrukcí jsou uchovávány v jednom zásobníku místo v několika registrech nebo jiných paměťových umístěních, jako ve většině programovacích jazyků .

Kód, který přidává dvě čísla v x86 assembleru , kde eax a edx specifikují dva různé registry pro všeobecné účely :

add eax, edx

Kód v intermediálním jazyce (IL), kde 0 je eax a 1 je 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

V druhém příkladu jsou hodnoty dvou registrů, eax a edx, nejprve vloženy do zásobníku. Když se zavolá add-instrukce, operandy se „vyskočí“ nebo načtou a výsledek se „posune“ nebo uloží do zásobníku. Výsledná hodnota se poté vysune ze zásobníku a uloží se do eax.

Objektově orientované koncepty

CIL je navržen tak, aby byl objektově orientovaný. Můžete vytvářet objekty, volat metody a používat jiné typy členů, například pole.

Každá metoda musí (až na několik výjimek) být umístěna ve třídě. Stejně tak tato statická metoda:

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

Metoda Add nevyžaduje deklaraci žádné instance Foo, protože je deklarována jako statická a poté ji lze v C # použít takto:

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

V CIL by to vypadalo takto:

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

Třídy instancí

Třída instance obsahuje alespoň jeden konstruktor a některé členy instance . Následující třída obsahuje sadu metod představujících akce Car-objektu.

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

Vytváření objektů

V instancích třídy C # se vytvářejí takto:

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

A tato prohlášení jsou zhruba stejná jako tyto pokyny v 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í instančních metod

Metody instance jsou vyvolány v C # jako následující:

myCar.Move(3);

Jak je vyvoláno v CIL:

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

Metadata

Common Language Infrastructure (CLI) zaznamenává informace o vypracovaných třídách jako metadata . Stejně jako knihovna typů v modelu komponentního objektu umožňuje aplikacím podporovat a objevovat rozhraní, třídy, typy, metody a pole v sestavě. Proces čtení takových metadat se nazývá „ reflexe “.

Metadata mohou být data ve formě „atributů“. Atributy lze přizpůsobit rozšířením Attribute třídy. Toto je výkonná funkce. Umožňuje tvůrci třídy možnost zdobit ji dalšími informacemi, které mohou spotřebitelé třídy používat různými smysluplnými způsoby, v závislosti na doméně aplikace.

Příklad

Níže je uveden základní program Hello, World napsaný v CIL. Zobrazí řetězec „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
}

Následující kód je složitější v počtu operačních kódů.

Tento kód lze také porovnat s odpovídajícím kódem v článku o Java bytecode .

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

V syntaxi CIL to vypadá takto:

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

Toto je jen reprezentace toho, jak CIL vypadá poblíž úrovně virtuálního počítače (VM). Při kompilaci jsou metody uloženy v tabulkách a pokyny jsou uloženy jako bajty uvnitř sestavení, což je Portable Executable (PE).

Generace

Sestava a pokyny CIL jsou generovány kompilátorem nebo obslužným programem nazvaným IL Assembler ( ILAsm ), který je dodáván s prostředím provádění.

Assembled CIL can also be disassembled into code again using the IL Disassembler (ILDASM). Existují další nástroje, jako je .NET Reflector, které mohou CIL dekompilovat do jazyka vyšší úrovně (např. C # nebo Visual Basic ). Díky tomu je CIL velmi snadným cílem pro reverzní inženýrství. Tento znak je sdílen s Java bytecode . Existují však nástroje, které mohou kód zmatit a udělat to tak, aby kód nemohl být snadno čitelný, ale aby byl stále spustitelný.

Provedení

Just-in-time kompilace

Just-in-time kompilace (JIT) zahrnuje přeměnu bajtového kódu na kód okamžitě spustitelný CPU. Převod se provádí postupně během provádění programu. Kompilace JIT poskytuje optimalizaci specifickou pro prostředí, bezpečnost typu běhového modulu a ověření sestavení. Aby toho bylo možné dosáhnout, kompilátor JIT prozkoumá metadata sestavení pro všechny nelegální přístupy a náležitě zpracovává porušení.

Předběžná kompilace

Prostředí kompatibilní s rozhraním CLI také přichází s možností provést kompilaci AOT ( Time-of-time ) sestavy, aby se spouštěla ​​rychleji odstraněním procesu JIT za běhu.

V .NET Framework existuje speciální nástroj zvaný Native Image Generator (NGEN), který provádí AOT. Odlišný přístup pro AOT je CoreRT, který umožňuje kompilaci kódu .Net Core do jednoho spustitelného souboru bez závislosti na běhovém prostředí. V Mono je také možnost udělat AOT.

Pokyny pro ukazatel - C ++ / CLI

Pozoruhodný rozdíl od bytecode v Javě je ten, že CIL přichází s ldind, stind, ldloca a mnoha instrukcemi volání, které jsou dostatečné pro manipulaci s ukazateli dat / funkcí potřebnými pro kompilaci kódu C / C ++ do 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();
}

Odpovídající kód v CIL lze vykreslit takto:

.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

Viz také

Reference

Další čtení

externí odkazy