# Classfile

Een classfile bevat bytecode die uitgevoerd kan worden door een Java Virtual Machine. Classfiles kunnen inmiddels worden geproduceerd door diverse compilers (Javac, JRuby, Scala, Groovy, Kotlin, Clojure, Jython, Fantom, Gosu, en andere), wanneer deze de broncode compileert.
Een classfile bestaat uit de volgende onderdelen:

## Header

De header bevat een magic number en versieinformatie. Een magic number is een constante aan het begin van een file die aangeeft dat de file een bepaald soort data bevat. Dit geldt niet alleen voor classfiles, maar ook voor veel andere bestandsformaten, bijvoorbeeld .GIF files en .ZIP files. Het magic number waarmee iedere classfile begint is 0xCAFEBABE.
Het magic number wordt gevolgd door 4 bytes die de versie van de gebruikte specificatie aangeven: eerst 2 bytes voor de minor version, dan 2 bytes voor de major version. Een header voor een classfile met versie 49.0 (J2SE 5.0) ziet er dan als volgt uit:

## Constantpool

De header wordt gevolgd door de constantpool. In de constantpool wordt een aantal constanten gedefinieerd die in de rest van de classfile gebruikt worden. De eerste 2 bytes duiden het aantal constanten in de constantpool aan.
Iedere waarde in de pool wordt voorafgegaan door een tag (1 byte) die het datatype aangeeft (en dus ook het aantal bytes dat de daaropvolgende waarde inneemt). Er zijn 11 tags, onder andere voor (UTF-8) strings, integers en verwijzingen naar methoden en fields.
Het begin van een constantpool kan er als volgt uitzien:
In dit geval bevat de pool 3 constanten (alleen de eerste wordt hier weergegeven). De eerste constante is een UTF-8 string (aangegeven door tag 1). In het geval van een string wordt de tag gevolgd door 2 bytes die de lengte van de string aangeven, in dit geval 5 bytes. Hierna volgt de string zelf: "hello", gecodeerd in hexidecimaal als 48656c6c6f.

## Klasse-informatie

Na de constantpool volgt er informatie over de klasse die door deze bytecodefile gedefinieerd wordt, zoals de naam en de superclass. Dit gedeelte bestaat uit 8 bytes, verdeeld in 4 items van elk 2 bytes:
* De access flags van de klasse, zoals "public", "abstract" of "final". Deze worden gecodeerd als een bitmask.
* De naam van de klasse. Dit is een verwijzing naar een item in de constantpool dat een string met de naam van de klasse bevat.
* De superklasse. Dit is weer een verwijzing naar een string in de constantpool.
* Het aantal interfaces dat deze klasse implementeert.
Als de klasse één of meer interfaces implementeert volgt vervolgens een lijst met de geïmplementeerde interfaces.

## Fields en methods

Deze sectie beschrijft de velden (fields, of attributen) en methoden van de klasse. De sectie begint met 2 bytes. Deze specificeren het aantal velden van de klasse. Vervolgens volgt de lijst met velden. Daarna komen er weer 2 bytes, die het aantal methoden van deze klasse aangeven. Deze worden op hun beurt weer gevolgd door de methoden.
Velden en methoden worden op dezelfde manier gecodeerd. Elk gedeelte dat een veld of een methode specificeert begint met 2 bytes die de access flags (zoals "static", "public", "abstract", enzovoort) aangeven, gecodeerd als bitmask. De volgende 2 bytes bevatten de naam van de methode of het veld. Dit is weer een verwijzing naar een string in de constantpool.

### Type descriptor

Hierna volgen nog 2 bytes die het type van de methode of het veld specificeren (de zogenaamde type descriptor). Het type van een veld geeft aan welk type waarden het veld kan bevatten. Het type van een methode geeft aan hoeveel argumenten de methode verwacht, van welke datatype deze argumenten zijn en welk datatype de methode retourneert.
Deze uit 2 bytes bestaande type descriptor is weer een verwijzing naar een string in de constantpool. Bijvoorbeeld: de string in de constantpool waarnaar de type-descriptor van de main-routine wijst is altijd ([Ljava/lang/String;)V. Dit geeft aan dat de main-routine een array (aangegeven door van strings (aangegeven door Ljava/lang/String) als argument verwacht en void (aangegeven door V) als returntype heeft.

### Attributen

Na de type-descriptor volgen eventuele attributen van de methode of het veld. Een veld kan bijvoorbeeld een ConstValue-attribuut hebben dat de beginwaarde van het veld specificeert. Meestal hebben velden echter geen attributen.
De sectie met attributen begint met 2 bytes die het aantal attributen voor deze methode of dit veld aangeven. Een attribuut wordt als volgt gecodeerd: eerst 2 bytes die weer naar een string in de constantpool verwijzen. Deze string bevat de naam van het attribuut. Vervolgens 4 bytes die de lengte van de data die bij het attribuut hoort aangeven.
De classfile specificatie maakt het mogelijk om zelf nieuwe attributen toe te voegen. De JVM die de classfile uitvoert negeert attributen die hij niet ondersteunt.

#### Het code-attribuut

Methoden hebben in meestal in ieder geval één attribuut: een Code-attribuut. Hierin wordt onder andere gespecificeert hoeveel items op de stack de methode (maximaal) gebruikt, en hoeveel lokale variabelen. Ook wordt in het Code-attribuut de implementatie van de methode (de body) opgenomen.
Na de body van de methode volgt nog informatie over de exceptions die de methode afhandeld. En ten slotte kan het Code-attribuut op zijn beurt ook nog weer attributen hebben. Die worden dan na de informatie over exceptions gespecificeerd.

#### Voorbeeld van een Code-attribuut

Het Code-attribuut van een methode kan er als volgt uitzien:
De bovenstaande methode bevat slechts één instructie: een return-statement (deze heeft opcode 5).

## Class attributen

De classfile eindigt met (nog) een lijst attributen. Deze gelden voor de hele class en worden op dezelfde manier gecodeerd als de attributen van velden en methodes. Ook hier geeft de specificatie de mogelijkheid om nieuwe attributen te implementeren. De specificatie zelf definieerd alleen het SourceFile-attribuut, die de naam van de file met broncode bevat waarvan de classfile afkomstig is (voor debuggers).

## Opcodes en instructies

Zoals we al zagen worden de instructies waaruit een methode bestaat opgeslagen in een Code-attribuut bij de betreffende methode. Omdat o.a. Java geen code buiten methodes (behalve declaraties en initialisatie-assignments) toestaat kunnen alle instructies waaruit een programma bestaat op deze manier in de classfile opgeslagen worden.
Broncode-statements en expressies worden hiervoor door de compiler vertaald naar reeksen zogenaamde bytecodes. Net als bij "echte" machinetaal bestaat een bytecode-instructie uit een opcode en een aantal argumenten, de operanden, waarbij het aantal operanden en hun lengte afhangt van de opcode.
Een serie bytecode-instructies kan er als volgt uitzien:
Het bovenstaande voorbeeld wordt aanschouwelijker als we het noteren als Java assembler:
Dit stukje bytecode telt 1 en 2 bij elkaar op en print het resultaat.
* ↑ Zie voor de overige 10 tags sectie 4.4 van The Java™ Virtual Machine Specification
* ↑ Zie sectie 4.1 van de specificatie voor de betekenis van de bits
* ↑ Zie sectie 4.5 van de specificatie voor de betekenis van de bits
* ↑ Zie sectie 4.7.3 van de specificatie
* The Java™ Virtual Machine Specification, 2e editie, online
* Programming for the Java Virtual Machine, Joshua Engel, Addison Wesley, 1999, ISBN 0201309726