ClassfileEen 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: HeaderDe 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 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: cafebabe00000031 ConstantpoolDe 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: 0003 De pool bevat 3 constanten. 01 De eerste constante is een string. 0005 De string is 5 bytes. 48656c6c6f "Hello". 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[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 Klasse-informatieNa 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:
Als de klasse één of meer interfaces implementeert volgt vervolgens een lijst met de geïmplementeerde interfaces. Fields en methodsDeze 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[3]. 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 descriptorHierna 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 AttributenNa de type-descriptor volgen eventuele attributen van de methode of het veld. Een veld kan bijvoorbeeld een 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-attribuutMethoden hebben in meestal in ieder geval één attribuut: een Na de body van de methode volgt nog informatie over de exceptions die de methode afhandeld[4]. 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-attribuutHet Code-attribuut van een methode kan er als volgt uitzien: 0003 De naam van dit attribuut is de derde string in de constantpool (in dit geval "Code"). 00000009 De data van dit attribuut is 9 bytes groot. 0002 Deze methode gebruikt 2 items op de stack. 0000 Deze methode gebruikt geen lokale variabelen. 00000001 De lengte van de code zelf (de implementatie van de methode) is 1 byte. b1 De body van de methode bestaat hier uit slechts 1 instructie, met opcode 5. De bovenstaande methode bevat slechts één instructie: een return-statement (deze heeft opcode 5). Class attributenDe 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 Opcodes en instructiesZoals we al zagen worden de instructies waaruit een methode bestaat opgeslagen in een 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: 1201 Opcode 18: Push item op de stack. Argument: de integer 1.
1202 Zelfde, maar push nu een 2 op de stack.
60 Opcode 96: Haal 2 items van de stack, tel ze op en zet het resultaat op de stack
3601 Opcode 54: Haal item van de stack en sla deze op in lokale variabele 1.
b20508 Opcode 178: Haal een verwijzing naar een statisch veld uit de constantpool,
in dit geval "
Het bovenstaande voorbeeld wordt aanschouwelijker als we het noteren als Java assembler: ldc 1 # 1 naar stack ldc 2 # 2 naar stack iadd # optellen, resultaat naar stack istore 1 # resultaat opslaan in lokale variabele getstatic java/lang/System/out Ljava/io/PrintStream; # ref naar System.out (type PrintStream) # op de stack iload 1 # inhoud lokale variabele naar stack invokevirtual java/io/PrintStream/println(I)V # System.out.println(1) # System.out en 1 komen van de stack Dit stukje bytecode telt 1 en 2 bij elkaar op en print het resultaat. Bronnen, noten en/of referenties
|
Portal di Ensiklopedia Dunia