Syvempi katsaus .NET-ohjelmien kääntämiseen

Tutustutaan seuraavaksi vähän tarkemmin C#-kääntäjän toimintaan sekä .NET-ajoympäristöön. Kuten luvussa Lähdekoodista prosessorille avattiin, C#-kääntäjä tuottaa oletuksena käyttöjärjestelmäriippumattomalla välikielellä kirjoitettua koodia. Tässä luvussa tutkitaan hieman tarkemmin dotnet-työkalun tuottamat tiedostot ymmärtääkseen, kuinka käyttöjärjestelmäriippumaton ohjelmatiedosto muuttuu ajon aikana käyttöjärjestelmä- ja prosessoririippuvaiseksi konekieleksi.

Luku syventyy .NET-ajoympäristön toimintaan ja paikoin ylittää Ohjelmointi 1 -kurssin tietovaatimukset. Luvussa oleviin tietoihin voi siis tutustua oman halun mukaan.

1. Projektin luominen

Tehdään aluksi uusi puhdas Jypeli-projekti dotnet-työkalulla. dotnet on tällä hetkellä käytössä olevan .NET-ajoympäristön komentorivityökalu, jolla C#-projektit voi luoda, kääntää ja ajaa.

  1. Varmistetaan, että Jypeli-projektin pohja on asennettu

    dotnet new -i Jypeli.Templates

    Tämä komento asentaa Jypeli.Templates-pakkauksen virallisesta NuGet.org-pakkausvarastosta.

    Kaikki käytössä olevat projektipohjat voi listata seuraavalla komennolla:

    dotnet new -l

    Tämä listaa muun muassa juuri asennetut Jypeli-projektipohjat:

    Templates                                     Short Name           Language    Tags
    --------------------------------------------  -------------------  ----------  ------
    ConsoleMain                                   ConsoleMain          [C#]        Jypeli
    Jypeli Betaversio                             FysiikkapeliBeta     [C#]        Jypeli
    Fysiikkapeli                                  Fysiikkapeli         [C#]        Jypeli
    Peruspeli                                     Peruspeli            [C#]        Jypeli
    Tasohyppelypeli                               Tasohyppelypeli      [C#]        Jypeli
  2. Luodaan uusi kansio ja siirrytään siihen mkdir ja cd -komennoilla

  3. Luodaan uusi projekti Fysiikkapeli-pohjasta

    dotnet new Fysiikkapeli -n Lumiukko

    Tämä luo uuden Lumiukko-nimisen kansion, johon laitetaan C#-projektitiedostot.

  4. Avaa Lumiukko.cs tekstieditorissa ja lisää siihen yksinkertainen pelikoodi

    using Jypeli;
    
    public class Lumiukko : PhysicsGame
    {
        public void LisaaPalloja()
        {
            for (int i = 0; i < 200; i++)
            {
                double d = RandomGen.NextDouble(5, 20);
                PhysicsObject pallo = new PhysicsObject(d, d, Shape.Circle);
                pallo.Position = RandomGen.NextVector(Level.BoundingRect);
                pallo.Color = RandomGen.NextColor();
                Add(pallo);
            }
        }
    
        public override void Begin()
        {
            GameObject p1 = new GameObject(10, 10, Shape.Circle);
            Add(p1);
            LisaaPalloja();
            Camera.ZoomToLevel();
        }
    }

Seuraavissa alaluvuissa tutustutaan .NET-ajoympäristöön tämän projektin kautta. Kaikki ajettavat komennot suoritetaan alkaen projektikansiosta.

2. C#-projektin rakenne

Jypelin-projektin luomisen jälkeen Lumiukko-kansiosta löytyvät seuraavat tiedostot:

.
├── Lumiukko.cs
├── Lumiukko.csproj
└── Ohjelma.cs

Yleisesti ottaen C#-projektit sisältävät kolmea eri tiedostotyyppiä.

2.1 Lähdekooditiedostot

Lähdekooditiedostot ovat .cs-päätteiset tiedostot, kuten aiemmissa luvuissa on pohjustettu. Oletuksena kaikki kansiossa olevat tiedostot sisällytetään kääntämiseen.

Lähdekooditiedostot voi myös jakaa alikansioihin ja tällä tavoin järjestää ohjelma järkeviin kokonaisuuksiin. Jypeli-projekteissa tätä tarvitaan harvemmin, mutta isoimmissa projekteista tästä on valtavasti hyötyä.

2.2 Projektin asetukset

.csproj-päätteinen tiedosto sisältää asetukset, jolla projekti käännetään. dotnet-työkalu käyttää tämän tiedoston tiedot muodostaakseen automaattisesti oikeita komentoja csc-kääntäjälle ja NuGet-pakkauksenhallintatyökalulle. Lisäksi asetustiedostoon voi määritellä erilaisia toimintoja, joita suoritetaan ennen kääntämistä tai sen jälkeen.

.csproj-tiedosto on tekstitiedosto, joka koostuu XML-elementeista. XML-elementti on tekstipari <T>...</T>. Elementti alkaa yleensä alkutagilla <T> ja päättyy lopetustagilla </T>. Tagien välissä oleva sisältö ... on elementin sisältö.
Jotkut elementit ovat muodossa <T/>, joilla ei ole sisältöä eikä lopetustagia.

Esimerkiksi Jypeli-projetkin projetkitiedosto näyttää seuraavalta:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <PublishReadyToRun>false</PublishReadyToRun>
        <TieredCompilation>false</TieredCompilation>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Jypeli.NET" Version="10.*" />
        <PackageReference Include="Jypeli.FarseerPhysics.NET" Version="1.0.*" />
    </ItemGroup>

</Project>

Tutustutaan joihinkin .csproj-tiedostossa oleviin asetuksiin. Kaikkien elementtien tarkka dokumentaatio löytyy .NET SDK -dokumentaatiosta.

2.2.1 TargetFramework: käytettävä .NET-ajoympäristö ja -rajapintaversio

Esimerkiksi

<TargetFramework>net5.0</TargetFramework>

määrittää, että

  • ohjelma on tarkoitettu ajettavaksi .NET 5 -ajoympäristössä
  • ohjelma käyttää .NET 5 -kirjastoja

C#-ohjelmat voi kääntää ja ajaa erilaisissa ajoympäristöissä. Tällä hetkellä seuraavat ajoympäristöt ja rajapintaversiot ovat olemassa ja tuetut C#-kielessä:

  • .NET Framework ja Mono: .NET Framework on alkuperäinen Microsoftin kehittämä ajoympäristö Windows-käyttöjärestelmille (julkaistu 2000). Mono on puolestaan .NET Frameworkin kanssa yhteensopiva, Windows- ja Unix-käyttöjärjestelmille kehitetty avoimen lähdekoodin .NET-ajoympäristö (julkaistu 2004). .csproj-tiedostossa sen tunnus on net20, net35, jne.
  • .NET Core on Microsoftin tukema avoimen lähdekoodin toteutus .NET-ajoympäristöstä (julkaistu 2016). Samoin kuin Mono, .NET Core on tarkoitettu Windows- sekä Unix-järjestelmille. .csproj-tiedostossa sen tunnus on netcoreapp1.0, netcoreapp2.0, jne.
  • .NET 5 on .NET Core -ajomypäristön tämänhetkinen versio. Alkaen tästä versiosta, .NET Core -ajoympäristö nimetään ".NET-ajoympäristöksi". .csproj-tiedostossa sen tunnus on net5.0
  • .NET Standard on määritelmä rajapinnoista (esim. System.Console, System.Text.StringBuilder), joita ajoympäristö toteuttaa. C#-koodi, joka on käännetty käyttäen .NET Standard -rajapintakokoelmaa voidaan ajaa ajoympäristöissä, jotka tukevat tämän kokoelman. Esimerkiksi .NET Standard 2.0 -rajapintakokoelmalle käännettyä C#-koodia voi ajaa NET Framework 4.6, .NET Core 3.0 ja Mono 5.4 -ympäristöissä ilman yhteensopivuusongelmia. .csproj-tiedostossa sen tunnus on netstandard1.0, netstandard1.1, jne.

On olemassa myös joitakin erikoiskäyttöön tarkoitettuja ajoympäristöjä. Kaikki eri toimivat ajoympäristöt ovat listattu Microsoft Docs -sivustolla.

2.2.2 PackageReference: ulkoisen kirjaston käyttäminen projektissa

Esimerkiksi

<PackageReference Include="Jypeli.NET" Version="10.*" />

määriittää, että

  • Projekti tarvitsee Jypeli.NET -nimisen pakkauksen NuGet-pakkauksenhallintajärjestelmästä
  • Projekti tarvitsee pakkauksen uusimman saatavilla olevan 10.x.x version

NuGet on dotnet-työkalun virallinen pakkauksenhallintatyökalu. Sen avulla .NET-kirjastot voidaan pakata ja julkaista verkkoon muiden kehittäjien käyttöön. Oletuksena dotnet-työkalu hakee pakkaukset NuGet.org-pakkausvarastosta.

2.3 Resurssit

Resurssitiedostot ovat erilaiset kuvat, tekstit ja äänet, jotka liitetään osaksi projektia. Näiden liittämiseksi on olemassa omat XML-elementit .csproj-tiedostoon.

3. C#-projektin kääntäminen dotnet-työkalulla

Tarkastellaan, miten projektin kääntäminen tapahtuu.

Projektin kääntäminen tehdään dotnet build -komennolla. Tämä käynnistää MSBuild-rakennusohjelman, joka tulkitsee .csproj sisällön ja ajaa tarvittavat komentoriviohjelmat.

Ajetaan dotnet build-komento, jolloin saadaan seuraava tuloste:

Microsoft (R) Build Engine version 16.9.0+57a23d249 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored XXX\Lumiukko\Lumiukko.csproj (in 4,88 sec).
  Lumiukko -> XXX\Lumiukko\bin\Debug\net5.0\Lumiukko.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:07.14

Kääntämisen olennaiset vaiheet:

  1. .csproj-tiedoston tulkinta

    MSBuild lukee .csproj-tiedoston sekä tarvittavat lisätiedostot, jos niitä on olemassa. Tässä tapauksessa tällaisia lisätiedostoja ei ole, joten tämä vaihe on varsin nopea.

  2. NuGet-pakkausten lataaminen ja kerääminen

    Kohdassa

    Determining projects to restore...
    Restored XXX\Lumiukko\Lumiukko.csproj (in 4,88 sec).

    MSBuild käyttää NuGet-työkalua ladatakseen kaikki PackageReference-pakkaukset ja niiden riippuvuudet.

  3. C#-tiedostojen kääntäminen

    Kohdassa

    Lumiukko -> XXX\Lumiukko\bin\Debug\net5.0\Lumiukko.dll

    MSBuild kutsuu csc-kääntäjän antaen sille parametreina NuGet-pakkauksien sisällä olevat kirjastot sekä TargetFramework-arvosta riippuvat rajapintakirjastot.

    Huomaa, että kääntäjä tuotti nimenomaan .dll-tiedoston, vaikka .csproj-asetuksissa projekti on ajettava ohjelma.
    Tähän palataan myöhemmin.

  4. Viimeistely

    Lopuksi MSBuild ajaa viimeistelytoimia riippuen .csproj-tiedostosta olevista asetuksista. MSBuild muun muassa kopioi ajoon tarvittavat kirjastot Lumiukko\bin\Debug\net5.0 -kansioon sekä muodostaa ajoa varten tarvittavat asetukset.

Halutessaan dotnet build-komenoon kaikki ajettavat komennot ja tapahtumat voi tutkiskella seuraavalla komennolla:

dotnet build -v d > build.log

Tämä luo build.log-tiedoston, joka sisältää tarkat tiedot kääntämisprosessita.

Kääntämisen lopuksi projektikansioon tulee kaksi kansiota lisää: obj ja bin. obj-kansio sisältää kääntämistä nopeuttavia väliaikaisia tiedostoja eikä siis ole kovin mielenkiintoinen. Sen sijaan bin sisältää lopullisen käännetyn ohjelman ja tarvittavat riippuvuudet. Tutkitaan seuraavaksi bin-kansion sisältö.

4. Käännetyn .NET-ohjelman rakenne

Tutkitaan edellisessä luvussa käännettyä Jypeli-kirjastolla tehtyä peliä. Käännetty peli löytyy kansiosta bin\Debug\net8.0 (.NET-version numero saattaa vaihdella). Kansion rakenne on seuraava:

.
├── Cyotek.Drawing.BitmapFont.dll
├── FontStashSharp.dll
├── Jypeli.FarseerPhysics.dll
├── Jypeli.dll
├── Lumiukko.deps.json
├── Lumiukko.dll
├── Lumiukko.exe
├── Lumiukko.pdb
├── Lumiukko.runtimeconfig.dev.json
├── Lumiukko.runtimeconfig.json
├── (lisää dll-tiedostoja)...
├── ref
│   └── Lumiukko.dll
└── runtimes
    ├── linux-x64
    ├── osx-arm64
    ├── osx-x64
    ├── win
    ├── win-x64
    └── win-x86                      

4.1 Ohjelmakansion tiedostot ja alikansiot

Kansio sisältää siis kaiken tarvittavan ohjelman ajamiseksi. Käydään läpi ensin "itsestäänselvät" tiedostot ja kansiot läpi.

Tiedostot

  • Cyotek.Drawing.BitmapFont.dll
  • FontStashSharp.dll
  • Jypeli.FarseerPhysics.dll
  • Jypeli.dll
  • Microsoft.Win32.SystemEvents.dll
  • StbImageSharp.dll
  • StbTrueTypeSharp.dll
  • System.Drawing.Common.dll
  • Silk.NET-alkuisia tiedostoja

ovat .NET-kirjastot, joita peli käyttää. Nämä tiedostot sisältävät järjestelmäriippumatonta ohjelmakoodia Jypelin toimintaa varten. Tiedostot ovat tulevat alun perin Jypeli.NET- ja Jypeli.FarseerPhysics.NET-pakkauksista (ja niiden riippuvuuksista), jotka määriteltiin .csproj-tiedostosta. Lisäksi Silk.NET-alkuiset dll-tiedostot tarjoavat käyttöliittymiä erilaisiin grafiikka- ja multimediateknologioihin, kuten OpenGL:ään ja Vulkaniin, joita peli saattaa hyödyntää grafiikan renderöimiseksi. Yhdessä nämä tiedostot mahdollistavat varsinaisen pelin ajamisen.

runtimes-kansio sisältää lisäksi käyttöjärjestelmästä riippuvia kirjastoja. Jypelin tapauksessa nämä ovat SDL-grafiikkakirjasto sekä OpenAL-äänikirjasto. Kansion sisältä löytyvät nämä kaksi kirjastoa käännettynä valmiiksi eri käyttöjärjestelmille. Nämäkin tiedostot tulevat NuGet-pakkauksista.

ref-kansio sisältää niin sanottuja viitekirjastoja, joita ensisijaisesti käytetään kääntämisprosessin nopeuttamiseksi. Viitekirjastoja voi käyttää myös oman projektin rajapinnan jakamiseksi muille ilman sitä, että jaettaisiin samalla itse ohjelmakoodia. Ajettavien ohjelmien tapauksessa tämän kansion sisältön on usein turha.

Loput tiedostot ovat Lumiukko-alkuiset. Nämä tiedostot liittyvät suoraan käännettyyn projektiin.

4.1.1 deps.json: lista ajonaikaisista riippuvuuksista

Lumiukko.deps.json sisältää .NET-ajoympäristölle tarkoitettua tietoa seuraavista asioista:

  • Mitä ajoympäristöä ohjelma voi käyttää
  • Millä asetuksilla ohjelma käännettiin
  • Mitä kirjastoja ohjelman ajamiseen tarvitaan
  • Mistä NuGet-pakkauksista kirjastot voi löytää ja miten niiden eheys tarkistetaan

.NET-ajoympäristö hyödyntää tätä tietoa kirjastojen lataamisessa, sillä oletuksena turvallisuussyistä ajoympäristöön pyritään lataamaan vain tarvittavat kirjastot. Tiedostoa käytetään myös siinä tapauksessa, jos kirjastot puuttuvat, jolloin ne voi ladata suoraan verkosta.

4.1.2 runtimeconfig.json ja runtimeconfig.dev.json: ajonaikaiset asetukset

Lumiukko.runtimeconfig.json sisältää tietoa, jota tarvitaan ohjelman käynnistämiseen. Tiedostosta löytyy muun muassa seuraavaa tietoa:

  • Mikä ajoympäristö tulee käyttää ohjelman käynnistämiseen
  • Miten ajoympäristö tulee käynnistää (esim. miten välikieli käännetään ohjelmakoodiksi)
  • Mistä poluista lisäkirjastot tulee etsiä

Toisin kuin Lumiukko.deps.json, ilman tätä asetustiedostoa .NET-ohjelma ei käynnisty.

Lisätietoja deps.json ja runtimeconfig.json -tiedostojen rakenteesta ja tarkoituksesta löytyy .NET-spesifikaatiosta.

4.1.3 .dll: käännetty C#-ohjelma

Lumiukko.dll sisältää itse käännetyn C#-ohjelman. C# on käännetty käyttöjärjestelmästä riippumattomaksi CIL-välikieleksi (Common Intermediate Language), jota .NET-ympäristö puolestaan tulkitsee ja kääntää ajon aikana lopulliseksi ajettavaksi koodiksi.

Ohjelman käynnistäminen onnistuu komennolla

dotnet Lumiukko.dll

Tällä komennolla tapahtuu seuraavaa:

  1. dotnet-työkalu käynnistää tyhjän .NET-ajoympäristön
  2. .NET-ajoympäristö lukee runtimeconfig.json ja deps.json -tiedostot ja konfiguroi itseään tietojen perusteella
  3. .NET-ajoympäristö lukee Lumiukko.dll-tiedoston, etsii sieltä aloitusaliohjelman Main() ja suorittaa sen
  4. Ajon aikana ajoympäristö saattaa ladata lisäkirjastoja käyttämällä runtimeconfig.json ja deps.json -tiedostossa olevia asetuksia.

4.1.4 .exe: .NET-ympäristön automaattinen käynnistysohjelma

Vaikka Lumiukko.dll sisältää itse ohjelmakoodin, ohjelman jatkuva käynnistäminen komentoriviltä voi olla hieman ärsyttävää. Tätä varten MSBuild laittoi kansioon myös klikattavan apuohjelman Lumiukko.exe. Apuohjelma ei sisällä yhtään projektin omaa ohjelmakoodia, vaan se yksinkertaisesti tekee saman asian kuin dotnet Lumiukko.dll.

Toisin sanoin Lumiukko.exe on apuohjelma, joka käynnistää .NET-ajoympäristön ja ajaa Lumiukko.dll-tiedoston. Apuohjelma on käyttöjärjestelmäriippuvainen, mutta se on sama kaikille .NET 5 -projekteille.

4.2 Käännetyn ohjelmatiedoston sisältö

Tutkitaan lopuksi hieman käännettyä Lumiukko.dll-tiedostoa. Kuten aiemmin on mainittu, tiedosto sisältää CIL-välikieleksi käännettyä koodia. Varmistetaan, että tämä pitää paikkaansa ja katsotaan hieman, miltä tämä CIL-välikieli näyttää.

Tätä varten tarvitaan jokin takaisinmallinnusohjelma, joka osaa lukea .NET-ajoympäristölle tarkoitetut .dll-tiedostot. Näistä yleisessä käytössä ovat ILSpy ja dnSpy. Tässä alaluvussa käytetään ILSpy-ohjelmaa, sillä sen tuloste on hieman parempi kuin dnSpy:n.

ILSpy 7.1
ILSpy 7.1

Ladataan Lumiukko.dll-tiedosto ILSpy-ohjelmaan (File \(\rightarrow\) Open \(\rightarrow\) Etsitään ja valitaan Lumiukko.dll ).

Ohjelman Assemblies-ikkunaan avautuu Lumiukko.dll. Sen sisältöä voi avata +/- -painikkeilla. Avataan tiedoston sisältö sen verran, että nähdään Lumiukko-luokka ja sen sisällä olevat aliohjelmat:

Lumiukko.dll ja laajennettuna ILSpy-ohjelmassa
Lumiukko.dll ja laajennettuna ILSpy-ohjelmassa

Huomataan siis, että ILSpy näkee samat aliohjelmat kuin mitä on määritelty luvun alussa.

Kun klikataan Begin()-aliohjelma, avautuu viereiseen ikkunaan Begin()-aliohjelma, jonka ILSpy käänsi CIL-välikielestä takaisin C#-kieleksi.

Lumiukko.Begin()-aliohjelma ILSpy-ohjelmassa
Lumiukko.Begin()-aliohjelma ILSpy-ohjelmassa

ILSpy on takaisinmallinnusohjelma, joka siis osaa lukea peliohjelmatiedoston sisällön ja kääntää se takaisin luettavaksi C#-ohjelmaksi. Huomaa, että CIL-välikielestä käännetty C# ei vastaa täysin alkuperäistä koodia. Esimerkiksi Begin-aliohjelman ensimmäiset kaksi riviä

GameObject p1 = new GameObject(10, 10, Shape.Circle);
Add(p1);

ovat liitetty yhteen, ILSpy-ohjelmassa muotoon

Add(new GameObject(10.0, 10.0, Shape.Circle));

sillä ILSpy yrittä "arvata" alkuperäisen ohjelman rakenteen.

Tutkitaan seuraavaksi, miltä itse CIL-välikieli näyttää. Tätä varten yläpalkin alasvetolaatikosta vaihdetaan C#-valinta IL with C# -valinnaksi:

Vaihdetaan C#-valinta IL with C# -valinnaksi
Vaihdetaan C#-valinta IL with C# -valinnaksi

Tämä näyttää seuraavan koodin:

.method public hidebysig virtual 
	instance void Begin () cil managed 
{
	// Method begins at RVA 0x20d8
	// Code size 58 (0x3a)
	.maxstack 3
	.locals init (
		[0] class [Jypeli]Jypeli.GameObject p1
	)

	// {
	IL_0000: nop
	// Add(new GameObject(10.0, 10.0, Shape.Circle));
	IL_0001: ldc.r8 10
	IL_000a: ldc.r8 10
	IL_0013: ldsfld class [Jypeli]Jypeli.Ellipse [Jypeli]Jypeli.Shape::Circle
	IL_0018: newobj instance void [Jypeli]Jypeli.GameObject::.ctor(float64, float64, class [Jypeli]Jypeli.Shape)
	IL_001d: stloc.0
	IL_001e: ldarg.0
	IL_001f: ldloc.0
	// (no C# code)
	IL_0020: call instance void [Jypeli]Jypeli.Game::Add(class [Jypeli]Jypeli.IGameObject)
	// LisaaPalloja();
	IL_0025: nop
	IL_0026: ldarg.0
	IL_0027: call instance void Lumiukko::LisaaPalloja()
	// base.Camera.ZoomToLevel();
	IL_002c: nop
	IL_002d: ldarg.0
	IL_002e: call instance class [Jypeli]Jypeli.Camera [Jypeli]Jypeli.Game::get_Camera()
	IL_0033: callvirt instance void [Jypeli]Jypeli.Camera::ZoomToLevel()
	// }
	IL_0038: nop
	IL_0039: ret
} // end of method Lumiukko::Begin

Tämä koodi on CIL-välikieli, johon meidän Begin()-ohjelma on käännetty. Mukana on laitettu kommentteina C#-ohjelman eri osat.

CIL-ohjelma koostuu yksinkertaisista komennoista, jotka suoritetaan samassa järjestyksessä kuin C# (eli ylhäältä alas). Yksi rivi C#-koodia vastaa useampaa CIL-välikielen komennoista. Esimerkiksi Begin-aliohjelmassa olevat kaksi riviä

GameObject p1 = new GameObject(10, 10, Shape.Circle);
Add(p1);

vastaavat CIL-välikieltä

ldc.r8 10
ldc.r8 10
ldsfld class [Jypeli]Jypeli.Ellipse [Jypeli]Jypeli.Shape::Circle
newobj instance void [Jypeli]Jypeli.GameObject::.ctor(float64, float64, class [Jypeli]Jypeli.Shape)
stloc.0
ldarg.0
ldloc.0
call instance void [Jypeli]Jypeli.Game::Add(class [Jypeli]Jypeli.IGameObject)

(tulosteesta siivottu turhat kommentit ja rivinumerot pois).

CIL-kieli on dokumentoitu hyvin laajasti ja on mahdollista lukea käsin. CIL-kieli on pinokieli, eli muuttujien sijaan kaikki tieto välitetään pinon kautta. Pinoa voi ajatella tässä tapauksessa korttipakkana: esimerkiksi ld- eli "load"-komennot laitavat "pakan päälle" jonkun arvon. Monet muut komennot puolestaan ottavat pinon päältä arvoja ja tekevät niillä asioita.

Tällä logiikalla yllä oleva koodipätkä voi tulkita pienellä vaivalla seuraavasti:

// Lataa pinoon luku 10 8-tavuisena reaalilukuna (eli double)
ldc.r8 10

// Lataa pinoon luku 10 8-tavuisena reaalilukuna (eli double)
ldc.r8 10  

// Lataa pinoon arvo Shape.Circle
ldsfld class [Jypeli]Jypeli.Ellipse [Jypeli]Jypeli.Shape::Circle 

// Ota pinosta 3 päällimmäistä arvoa (luku 10.0, luku 10.0 ja Shape.Circle), tee niillä uusi GameObject ja laita se pinon päälle
newobj instance void [Jypeli]Jypeli.GameObject::.ctor(float64, float64, class [Jypeli]Jypeli.Shape)

// Ota pinosta päällimmäinen arvo (luotu GameObject) ja tallenna se paikalliseen muuttujaan 0 (eli p1 C#-koodissa)
stloc.0

// Lataa pinoon nykyinen peli (Lumiukko)
ldarg.0

// Lataa pinoon paikallinen muuttuja 0 (eli p1 C#-koodissa, joka on GameObject)
ldloc.0

// Ota pinosta yksi arvo (eli GameObject) sekä peli, johon halutaan vaikuttaa (Lumiukko) ja kutsu Add-aliohjelma
call instance void [Jypeli]Jypeli.Game::Add(class [Jypeli]Jypeli.IGameObject)

Eri komentojen toiminnot voi katsoa suoraan ILSpy-ophjelmasta viemällä kursori komennon päälle:

Kun vie kursorin CIL-komennon päälle, ILSpy näyttää komennon selityksen
Kun vie kursorin CIL-komennon päälle, ILSpy näyttää komennon selityksen

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.