Java-tavukoodi Pekka Parviainen Helsinki 1.4.2005 Ohjelmointikielten kääntäjät -kurssin seminaarityö Helsingin yliopisto Tietojenkäsittelytieteen laitos
Sisällys 1 Johdanto...1 2 Java-tavukoodin periaatteet...1 2.1 Pinoperustaisuus...3 2.2 Tyypitys...3 2.3 Class-tiedostot...4 3 Yhteenveto...6 Lähteet...7
1 Johdanto Java on yleiskäyttöinen, rinnakkainen ja luokkapohjainen olio-ohjelmointikieli [GJS00, 1]. Alunperin Java-projektin tavoitteena oli tuottaa ohjelmistoympäristö pienille hajautetuille sulautetuille järjestelmille [Gos95, 111], mutta nykyään Javaa käytetään laajasti eri sovellusalueilla. Java-kielinen lähdekoodi käännetään yleensä välikieliseksi koodiksi, jota kutsutaan tavukoodiksi (bytecode). Tavukoodi tallennetaan yleensä class-tiedostoon. Joskus class-tiedostosta puhutaan jopa synonyyminä tavukoodille (esim. [Wik99]). Javan hallinnoija Sun Microsystems määrittelee Java-tavukoodin (tässä dokumentissa luhennettynä tavukoodi) seuraavasti : Tavukoodi on Java-kääntäjän generoimaa alustariippumatonta koodia, jota Java-tulkki suorittaa [Sun05]. Java-tulkin ymmärtämää tavukoodia voidaan tuottaa myös muista kielistä kuin Javasta, joten ei ole tarkoituksenmukaista rajoittaa määritelmää pelkästään Java-kääntäjään. Tässä dokumentissa käytetään seuraavaa määritelmää: Javatavukoodi on alustariippumatonta koodia, jota voidaan suorittaa Java-tulkilla. Tämä tutkielma antaa lyhyen johdatuksen Java-tavukoodin käskykantaan ja toimintaan. Koska Javatavukoodi class-tiedostot liittyvät läheisesti tavukoodiin ja tavukoodia suoritetaan yleensä Javavirtuaalikoneessa, käsitellään näitä asioita sen verran kuin se on asian havainnollistamiseksi tarpeen. 2 Java-tavukoodin periaatteet Java-tavukoodi kuuluu ns. välikieliin eli se muistuttaa assembler-kieliä, mutta on kuitenkin hiukan korkeammalla abtraktiotasolla. Tavukoodissa kukin operaatiokoodi (opcode) on yhden tavun mittainen. Mikäli käsky ei ota kaikkia parametrejaan pinosta, tallennetaan parametrit tavukoodissa operaatiokoodin perään. Numeeriset arvot tallennetaan tavukoodissa samaa formaatissa kuin java.io.datainputstream-luokassa. Koska Java-tavukoodin tavallisin suorittaja on Java-virtuaalikone, vastaa tavukoodin käskykanta virtuaaliprosessorin käskykantaa. Käskykannan jakautuminen eri käskykategorioihin näkyy taulukosta 1. 1
Taulukko 1 Käskyt kategorioittain Kategoria Käskyjen lkm Esimerkkejä Aritmeettiset operaatiot 24 iadd, lsub, frem Loogiset operaatiot 12 iand, lor, ishl Tyyppimuunnokset 15 int2short, f2l, d2i Vakioiden lisäys pinoon 20 bipush, sipush, ldc, iconst_0, fconst_1 Pinon manipulointi 9 pop, pop2, dup, dup2 Kontrollin siirtokäskyt 28 goto, ifne, ifge, if_null, jsr, ret Paikallisten muuttujien hallinta 52 astore, istore, aload, iload, aload_0 Taulukoiden käsittely 17 aastore, bastore, aaload, baload Olioiden ja taulukoiden luonti 4 new, newarray, anewarray, multianewarray Olioiden käsittely 6 getfield, putfield, getstatic, putstatic Metodikutsu ja paluu 10 invokevirtual, invokestatic, areturn Sekalaiset 5 throw, monitorenter, breakpoint, nop Lähde : MeD97, 6-7 Kuten taulukosta 1 näkyy, käskykannassa on paljon käskyjä objektien luomiseen ja käsittelyyn. Nämä käskyt ovat paljon monimutkaisempia kuin todellisten prosessorien käskyt ja tästä on helppo huomata, että virtuaalikone toimii korkeammalla abstraktiotasolla kuin tavallinen prosessori [MeD97, 6]. Javavirtuaalikone käyttää hyväkseen tietoa eräiden operandien (esim. int-vakiot -1, 0, 1, 2, 3, 4 ja 5 iconst_<i> käskyjen yhteydessä) yleisyydestä tekemällä näistä operandeista osan operaatiokoodia. Tällä tavoin säästetään niin aikaa kuin tilaakin. Esimerkiksi jos halutaan lisätä pinoon int-vakio 0, voidaan se tehdä helposti käskyllä iconst_0. Toisaalta sama asia voitaisiin toteuttaa käskyllä bipush 0. Tällöin tosin koodi olisin tavun pidempi, sillä vakio 0 olisi tallennettava tavukoodiin operaatiokoodin perään. Myös ohjelman suoritusaika kasvaisi, sillä virtuaalikoneen olisi haettava operandi muistista käskyn suorittamisen aikana [LiY99]. Kahdessa seuraavassa alaluvussa käsitellään kahta Java-virtuaalikoneen olennaista ominaisuutta: pinoperustaisuutta ja tyypitystä. Näiden jälkeen katsastetaan lyhyesti class-tiedoston rakennetta. 2
2.1 Pinoperustaisuus Java-virtuaalikone on pinoperustainen, joten useimmat käskyt ottavat yhden tai useamman parametrin virtuaalikoneen sillä hetkellä suorittaman kehyksen operandipinosta tai palauttavat tuloksen pinon päälle. Virtuaalikone luo uuden kehyksen aina kun kutsutaan uutta metodia. Näin ollen kullakin hetkellä voi olla olemassa useita kehyksiä (yksi jokaista sisäkkäistä metodikutsua kohti). Kullakin kehyksellä on oma pinonsa ja paikalliset muuttujansa. Ainoastaan kyseisellä hetkellä suoritettavan kehyksen operandipino on aktiivinen [LiY99]. Virtuaaliprosessori käyttää numeroituja "paikallisia muuttujia" väliaikaiseen tallennukseen rekisterien sijasta [MeD97, 6]. 2.2 Tyypitys Java käyttää vahvaa tyypitystä. Java-tavukoodissa tyypit jaetaan alkeistyyppeihin ja viittaustyyppeihin. Alkeistyyppejä ovat numeeriset tyypit byte, short, int, long, char, float ja double sekä totuusarvotyyppi boolean ja paluuosoitetyyppi returnaddress. Tyypin returnaddress arvot ovat osoittimia käskyjen operaatiokoodeihin eikä niillä ole vastinetta Java-ohjelmointikielessä. Viittaustyypit jakautuvat luokkatyyppeihin, taulukkotyyppeihin ja rajapintatyyppeihin. Niiden arvot ovat viitteitä dynaamisesti luotuihin luokan ilmentymiin, taulukoihin tai rajapinnan toteuttaviin luokan ilmentymiin ja taulukoihin [LiY99]. Useimmissa pinopohjaisissa käskykannoissa pinolle ja paikallisille muuttujille voi tehdä lähestulkoon mitä vain. Java-tavukoodissa on tässä kohtaa tärkeä rajoite: Kullakin hetkellä jokaisella pinon alkiolla ja paikallisella muuttujalla on tyyppi. Tätä tyyppi-informaatiokokoelmaa kutsutaan kehyksen tyyppitilaksi (type state). Kunkin hetkinen tyyppitila voidaan päätellä staattisesti induktiolla, koska kaikilla käskyillä on seuraava ominaisuus: Kun tiedetään tyyppitila ennen käskyn suorittamista, voidaan yksiselitteisesti päätellä tyyppitila käskyn suorituksen jälkeen [Gos95, 112]. Java-virtuaalikone erottaa operandien tyypit käyttämällä eri tavukoodeja riippuen operandien tyypeistä 3
(esim. iadd, ladd, fadd ja dadd ovat kaikki virtuaalikoneen käskyjä, jotka laskevat yhteen kaksi numeerista arvoa ja palauttavat numeerisen tuloksen, mutta kukin on erikoistunut yhteen operandityyppiin eli ensimmäinen käsittelee int-arvoja, toinen long-arvoja, kolmas float-arvoja ja neljäs double-arvoja) [LiY99]. Java-virtuaalikoneen käskykanta ei ole ortogonaalinen eli operaatiot, jotka ovat käytössä yhdelle tyypille eivät välttämättä ole käytössä muilla tyypeillä. Ortogonaalisuuden puutetta perustellaan sillä, että 8-bittisiä operaatiokoodeja ei ole tarpeeksi, jotta kaikille Javan tyypeille voitaisiin tarjota sama tuki [MeD97, 56-67]. Parhaiten tuettu tyyppi on int. Toisaalta tyypeille byte, char ja short ei ole suoraa tukea. Tämä ei kuitenkaan ole suuri ongelma, sillä virtuaalikone muuttaa kyseiset tyypit tyypiksi int ja tämän jälkeen niitä voidaan käsitellä int-operaatioilla [LiY99]. 2.3 Class-tiedostot Java-tavukoodi tallennetaan tyypillisesti class-tiedostoihin. Class-tiedostojen tiedostopääte on ".class". Kukin class-tiedosto määrittelee yhden luokan, abstraktin luokan tai rajapinnan. Class-tiedosto sisältää paljon kriittistä dataa kuten luokan määrittelemien metodien tavukoodin, symbolisen viittauksen luokan yliluokkaan, listan luokassa määritellyistä kentistä sekä vakioaltaan, jossa on tiedot luokan käyttämistä symboleista ja literaaleista [MeD97, 71-72]. Informaatio on tallennettu class-tiedostoissa binaarimuodossa, jota käsitellään virtana 8-bittisiä tavuja. 16-, 32- ja 64-bittiset datakokonaisuudet tallennetaan big-endian -muodossa eli merkitsevin tavu ensin [Med97, 72]. Class-tiedosto muodostuu useista sisäkkäisistä taulukoista. Class-tiedoston ylimmän tason taulukon rakenne on seuraava [LiY99]: ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; 4
} cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; Taulukossa näkyvät tietotyypit u2 ja u4 tarkoittavat, että kyseiset arvot vievät kaksi ja neljä tavua tilaa. Kenttä magic sisältää "taikanumeron", jonka avulla tiedosto tunnistetaan class-tiedostoksi. Sen arvo on 0xCAFEBABE. Kentät minor_version ja major_version ilmaisevat yhdessä käytettävän classtiedostomuodon version. Kentän constant_pool_count arvo on yhtä suurempi kuin vakioaltaan alkioiden lukumäärä. Kenttä constant_pool muodostaa luokan vakioaltaan. Vakioaltaan alkiot edustavat String-vakioita, luokkien ja rajapintojen nimiä, kenttien nimiä sekä muita vakioita, joihin viitataan kyseisestä luokasta. Kentän access_flags bitit ilmaisevat luokan käyttöoikeudet ja joitain sen ominaisuuksia. Kentässä on seuraavat liput (flag): ACC_PUBLIC ACC_FINAL ACC_SUPER ACC_INTERFACE ACC_ABSTRACT luokka kaikkien käytettävissä aliluokkien luominen kielletty kohtele yliluokan metodeita erityistavalla kutsuttaessa invokespecialkäskyllä rajapintaluokka luokasta ei voi tehdä ilmentymiä 5
Kenttä this_class sisältää vakioaltaan indeksin. Indeksin määrittelemässä kohdassa sijaitsee tämän luokan nimi. Myös kenttä super_class sisältää vakioaltaan indeksin. Tämä indeksi määrittelee tämän luokan yliluokan nimen. Kenttä interfaces_count ilmaisee luokan toteuttamien rajapintojen lukumäärän. Kenttä interfaces on taulukko, jossa on kaikki luokan toteuttamat rajapinnat. Kussakin alkiossa on vakioaltaan indeksi, jonka osoittamasta kohdasta löytyy toteutetun rajapinnan nimi. Kenttä fields_count ilmaisee kentässä fields olevan taulun alkioiden lukumäärän. Kenttässä fields on taulu, jonka kukin alkio sisältää luokassa määritellyn kentän kuvauksen. Kenttä methods_count sisältää luokassa määriteltyjen metodien lukumäärän. Kenttä methods sisältää taulukon, jonka alkioina ovat luokassa määriteltyjen metodien kuvaukset. Kenttä attributes_count sisältää kentässä attributess sijaitsevan taulukon alkioiden lukumäärän. Kenttä attributes sisältää taulukon ominaisuusalkioita. Tarkempi kuvaus class-tiedoston rakenteesta löytyy esimerkiksi teoksista [MeD97] ja [LiY99]. 3 Yhteenveto Java-tavukoodi on abstraktiotasoltaan tavallista konekieltä ylempänä. Tavukoodia suoritetaan yleensä pinoperusteisessa virtuaalikoneessa. Tavukoodi käyttää paljon tyyppi-informaatiota ja se tallennetaan yleensä class-tiedostoon. 6
Lähteet Gos95 Gosling, James 1995. Java Intermediate Bytecodes. ACM SIGPLAN Workshop on Intermediate Representation (IR '95). GJS00 Gosling, James & Joy, Bill & Steele, Guy & Bracha, Gilad 2000. The Java tm Language Specification, Second Edition. Addison-Wesley. LiY99 Lindholm, Tim & Yellin, Frank 1999. The Java tm Virtual Machine Specification, Second Edition. Addison-Wesley. MeD97 Meyer, Jon & Downing, Troy 1997. Java Virtual Machine. O'Reilly & Associates, Inc., Sebastopol. Sun05 Sun Microsystems, Inc. 30.3.2005. J2SE Glossary. [WWW-dokumentti] <http://java.sun.com/docs/glossary.html>. Wik99 Wikla, Arto 1999. Ohjelmoinnin perusteet Java-kielellä. 4. painos, OtaDATA, Hämeenlinna. 7