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:
- Zdrojový kód se převede na byte kód CIL a vytvoří se sestavení CLI .
- 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.
- Procesor počítače provede nativní kód.
Instrukce
Bajtový kód CIL obsahuje pokyny pro následující skupiny úkolů:
- Načíst a uložit
- Aritmetický
- Převod typu
- Vytváření a manipulace s objekty
- Správa zásobníku operandů (push / pop)
- Přenos řízení (větvení)
- Vyvolání metody a vrácení
- Házení výjimek
- Souběžnost založená na monitoru
- Manipulace s ukazateli dat a funkcí potřebná pro C ++ / CLI a nebezpečný C # kód
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 = ¶m;
*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 = ¶m
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í
- Bock, Jason (2002). Programování CIL: Pod kapotou .NET . Apress. ISBN 978-1590590416 .