Common Intermediate Language
Common Intermediate Language (CIL), tidligere Microsoft Intermediate Language (MSIL) er det laveste niveau af menneskelig-læsbar programkode defineret i specifikationen af Common Language Infrastructure (CLI) og anvendes af .NET Framework og Mono. Programmeringssprog der er målrettet et CLS-kompatibelt kørselsmiljø kompileres til CIL, der samles til objektkode i et bytekode-lignende format. CIL er et objektorienteret assemblersprog og er fuldstændig stakbaseret. Dets bytekode fortolkes til maskinkode eller afvikles af en virtuel maskine.
CIL anvendes til oversættelse af programmeringssprogene C#, Visual C++, VB.NET og J# til et maskinkodelignende sprog, for anvendelse i et NET Framework.
Generel information
Under kompileringen af .NET-programmeringssprog, bliver kildekoden oversat til CIL-kode i stedet for platforms- eller processeror-specifik objektkode. CIL er et CPU- og platforms-uafhængigt instruktionssæt der kan udføres i ethvert miljø der understøtter Common Language Infrastructure, såsom .NET runtime på Windows, eller det platformsuafhængige Mono runtime. I teorien overflødiggør det nødvendigheden af forskellige udgivelser af eksekverbare filer til forskellige platforme og CPU-typer. CIL verificeres for sikkerhedsfejl under kørslen, hvilket giver øget stabilitet og pålidelighed sammenlignet med eksekverbare filer oversat direkte til maskinkode.
Afviklingsprocessen ser således ud:
- Kildekoden konverteres til Common Intermediate Language, der kan betragtes som CIL's ækvivalente til assemblersprog for en CPU.
- CIL bliver dernæst samlet til en form for bytekode og en .NET-assembly oprettes.
- Under kørslen af en .NET-assembly bliver dens kode sendt gennem .NET-runtimes JIT-kompiler hvor den oversættes til maskinkode. Det er også muligt at prekompilere koden, hvilket overflødiggør dette trin på bekostning af portabiliteten af den eksekverbare fil.
- Maskinkoden bliver afviklet af computerens processor.
Instruktioner
CIL bytekode har instruktioner for følgende grupper af opgaver:
- Hent og gem
- Aritmetik
- Typekonvertering
- Oprettelse og manipulation af objekter
- Styring af operander i stakken
- Kontroloverførsel (branching)
- Metodekald og returnering
- Signalering af undtagelser (exceptions)
- Overvågningsbaseret synkronisering
Kompositorisk model
Common Intermediate Language er objektorienteret og stakbaseret. Det betyder at data lægges i stakken (push), i stedet for at blive udtrukket fra et register som i de fleste CPU-arkitekturer.
I x86 kunne det se således ud:
add eax, edx
Den tilsvarende kode i IL kan gengives sådan:
ldloc.0
ldloc.1
add
stloc.0 // a = a + b eller a += b;
Her er der to lokale værdier der lægges i stakken (push). Når add-instruktionen bliver trukket ud af stakken hentes (poppes) de to operander og resultatet skubbes (pushes). Den tilbageværende værdi bliver trukket af stakken og lagret i den første lokale variabel.
Objektorienterede koncepter
Det kan udvides til at omfatte ojektorienterede koncepter. Du kan skabe objekter, kalde metoder og bruge andre typer af klassemedlemmer såsom felter.
CIL er designet til at være objektorienteret og alle metoder skal (med enkelte undtagelser) tilhøre en klasse. Således tilhører denne statiske metode, Add, en klasse, Foo:
.class public Foo
{
.method public static int32 Add(int32, int32) cil managed
{
.maxstack 2
ldarg.0 // indlæs det første argument
ldarg.1 // indlæs det andet argument
add // læg argumenterne sammen
ret // returner resultatet
}
}
Denne metode behøver ikke nogen instans (et objekt) af Foo fordi den er statisk. Det betyder, at den tilhører klassen og kan bruges således i C#:
int r = Foo.Add(2, 3); // 5
I CIL bliver det:
ldc.i4.2
ldc.i4.3
call int32 Foo::Add(int32, int32)
stloc.0
Instantierede klasser
En instantieret klasse indeholder som minimum en konstruktør (eng: constructor) og nogen instansmedlemmer. Denne klasse har et sæt metoder der repræsenterer handlinger for et Car-objekt.
.class public Car
{
.method public specialname rtspecialname instance void .ctor(int32, int32) cil managed
{
/* Kontruktør (constructor) */
}
.method public void Move(int32) cil managed
{
/* Implementering udeladt */
}
.method public void TurnRight() cil managed
{
/* Implementering udeladt */
}
.method public void TurnLeft() cil managed
{
/* Implementering udeladt */
}
.method public void Brake() cil managed
{
/* Implementering udeladt */
}
}
Oprettelse af objekter
I C# oprettes klasseinstanser således:
Car myCar = new Car(1, 4);
Car yourCar = new Car(1, 3);
Disse udtryk er groft sagt de samme som disse instruktioner:
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);
Invokering af instansmetoder
Instansmetoder kaldes som i efterfølgende:
myCar.Move(3);
I CIL:
ldloc.0 // Indlæs objektet "myCar" på stakken
ldc.i4.3
call instance void Car::Move(int32)
Følgende kode er mere kompleks i antal opkoder.
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);
}
}
I CIL ville det se nogenlunde således ud:
.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
}
Dette er blot en repræsentation af hvordan CIL ser ud nær VM-niveau. Efter kompileringen af koden gemmes metoderne i tabeller og instruktionerne er gemt som bytes indeni assemblien, der fungerer som Portabel Eksekverbar-fil (PE).