SezioniModifica
Ci sono 10 sezioni di base nella struttura del file di classe Java:
- Magic Number: 0xCAFEBABE
- Versione del formato del file di classe: le versioni minori e maggiori del file di classe
- Constant Pool: Pool di costanti per la classe
- Flags di accesso: per esempio se la classe è astratta, statica, ecc.
- Questa classe: Il nome della classe corrente
- Super Classe: Il nome della super classe
- Interfacce: Qualsiasi interfaccia nella classe
- Campi: Qualsiasi campo nella classe
- Metodi: Qualsiasi metodo nella classe
- Attributi: Qualsiasi attributo della classe (per esempio il nome del file sorgente, ecc.)
Magic NumberEdit
I file della classe sono identificati dalla seguente intestazione di 4 byte (in esadecimale): CA FE BA BE
(le prime 4 voci della tabella sottostante). La storia di questo numero magico è stata spiegata da James Gosling riferendosi a un ristorante di Palo Alto:
“Andavamo a pranzo in un posto chiamato St Michael’s Alley. Secondo la leggenda locale, nel profondo e oscuro passato, i Grateful Dead si esibivano lì prima di diventare famosi. Era un posto piuttosto funky che era decisamente un Grateful Dead Kinda Place. Quando Jerry morì, costruirono anche un piccolo santuario buddista. Quando ci andavamo, ci riferivamo al posto come Cafe Dead. Da qualche parte lungo la linea si è notato che questo era un numero HEX. Stavo rinnovando il codice del formato dei file e avevo bisogno di un paio di numeri magici: uno per il file degli oggetti persistenti e uno per le classi. Ho usato CAFEDEAD per il formato del file oggetto, e cercando parole esadecimali di 4 caratteri che andassero bene dopo “CAFE” (sembrava essere un buon tema) mi sono imbattuto in BABE e ho deciso di usarlo; a quel tempo, non sembrava terribilmente importante o destinato ad andare da qualche parte se non nel cestino della storia. Così CAFEBABE divenne il formato dei file di classe, e CAFEDEAD era il formato degli oggetti persistenti. Ma la struttura dell’oggetto persistente se ne andò, e con essa l’uso di CAFEDEAD – alla fine fu sostituito da RMI.
Layout generaleEdit
Perché il file di classe contiene elementi di dimensioni variabili e non contiene anche offset di file incorporati (o puntatori), è tipicamente analizzato in modo sequenziale, dal primo byte verso la fine. Al livello più basso il formato del file è descritto in termini di alcuni tipi di dati fondamentali:
- u1: un intero senza segno a 8 bit
- u2: un intero senza segno a 16 bit in ordine byte big-endian
- u4: un intero senza segno a 32 bit in ordine byte big-endian
- tabella: un array di elementi di lunghezza variabile di qualche tipo. Il numero di elementi nella tabella è identificato da un numero di conteggio precedente (il conteggio è un u2), ma la dimensione in byte della tabella può essere determinata solo esaminando ciascuno dei suoi elementi.
Alcuni di questi tipi fondamentali sono poi reinterpretati come valori di livello superiore (come stringhe o numeri in virgola mobile), a seconda del contesto.Non c’è applicazione dell’allineamento delle parole, e quindi non vengono mai usati byte di riempimento.Il layout complessivo del file di classe è quello mostrato nella tabella seguente.
byte offset | dimensione | tipo o valore | descrizione |
---|---|---|---|
0 | 4 byte | u1 = 0xCA hex |
numero magico (CAFEBABE) usato per identificare il file come conforme al formato class file |
1 | u1 = 0xFE hex |
||
2 | u1 = 0xBA hex |
||
3 | u1 = 0xBE hex |
||
4 | 2 byte | u2 | numero versione minore del formato di file di classe utilizzato |
5 | |||
6 | 2 byte | u2 | numero di versione maggiore del formato di file di classe utilizzato.
Java SE 17 = 61 (0x3D hex), |
7 | |||
8 | 2 byte | u2 | conteggio pool costante, numero di voci nella seguente tabella pool costante. Questo conteggio è almeno uno maggiore del numero effettivo di voci; vedere la discussione seguente. |
9 | |||
10 | cpsize (variabile) | table | constant pool table, un array di voci constant pool di dimensioni variabili, contenenti elementi come numeri letterali, stringhe e riferimenti a classi o metodi. Indicizzata a partire da 1, contenente (conteggio del pool di costanti – 1) numero di voci in totale (vedi nota). |
… | |||
… | |||
… | |||
10+cpsize | 2 byte | u2 | flag di accesso, una bitmask |
11+cpsize | |||
12+cpsize | 2 byte | u2 | identifica questa classe, indice nel pool di costanti ad una voce di tipo “Classe” |
13+cpsize | |||
14+cpsize | 2 byte | u2 | identifica la super classe, indice nel pool costante di una voce di tipo “Classe” |
15+cpsize | |||
16+cpsize | 2 byte | u2 | conteggio interfacce, numero di voci nella seguente tabella di interfaccia |
17+cpsize | |||
18+cpsize | isize (variabile) | table | interface table: un array di lunghezza variabile di indici di pool costanti che descrivono le interfacce implementate da questa classe |
… | |||
… | |||
… | |||
18+cpsize+isize | 2 byte | u2 | conteggio campi, numero di voci nella seguente tabella dei campi |
19+cpsize+isize | |||
20+cpsize+isize | fsize (variabile) | table | tabella dei campi, array di campi di lunghezza variabile
ogni elemento è una struttura field_info definita in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 |
… | |||
… | |||
… | |||
20+cpsize+isize+fsize | 2 byte | u2 | conteggio metodo, numero di voci nella seguente tabella dei metodi |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize (variabile) | table | method table, array di metodi di lunghezza variabile
ogni elemento è una struttura method_info definita in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6 |
… | |||
… | |||
… | |||
22+cpsize+isize+fsize+msize | 2 byte | u2 | conteggio attributi, numero di voci nella seguente tabella degli attributi |
23+cpsize+isize+fsize+msize | |||
24+cpsize+isize+fsize+msize | asize (variabile) | table | tabella degli attributi, array di attributi di lunghezza variabile
ogni elemento è una struttura attribute_info definita in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7 |
… | |||
… | |||
… |
Rappresentazione in un linguaggio di programmazione simile al CEdit
Siccome il C non supporta array multipli di lunghezza variabile all’interno di una struct, il codice che segue non verrà compilato e serve solo come dimostrazione.
struct Class_File_Format { u4 magic_number; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces; u2 fields_count; field_info fields; u2 methods_count; method_info methods; u2 attributes_count; attribute_info attributes;}
Il pool di costantiEdit
La tabella del pool di costanti è dove la maggior parte dei valori costanti letterali sono memorizzati. Questo include valori come numeri di tutti i tipi, stringhe, nomi di identificatori, riferimenti a classi e metodi, e descrittori di tipo. Tutti gli indici, o riferimenti, a specifiche costanti nella tabella delle costanti sono dati da numeri a 16 bit (tipo u2), dove il valore dell’indice 1 si riferisce alla prima costante nella tabella (il valore dell’indice 0 non è valido).
A causa di scelte storiche fatte durante lo sviluppo del formato del file, il numero di costanti nella tabella delle costanti non è effettivamente lo stesso del conteggio delle costanti che precede la tabella. Innanzitutto, la tabella è indicizzata a partire da 1 (invece che da 0), ma il conteggio dovrebbe essere interpretato come l’indice massimo più uno. Inoltre, due tipi di costanti (lunghe e doppie) occupano due slot consecutivi nella tabella, anche se il secondo slot è un indice fantasma che non viene mai usato direttamente.
Il tipo di ogni elemento (costante) nel pool di costanti è identificato da un tag iniziale di byte. Il numero di byte che segue questo tag e la loro interpretazione dipendono poi dal valore del tag. I tipi di costante validi e i loro valori di tag sono:
Tag byte | Byte aggiuntivi | Descrizione della costante | Versione introdotta |
---|---|---|---|
1 | 2+x byte (variabile) |
StringaUTF-8 (Unicode): una stringa di caratteri preceduta da un numero di 16 bit (tipo u2) che indica il numero di byte nella stringa codificata che segue immediatamente (che può essere diverso dal numero di caratteri). Si noti che la codifica usata non è in realtà UTF-8, ma comporta una leggera modifica della forma di codifica standard Unicode. | 1.0.2 |
3 | 4 byte | Integer: un numero a due complementi firmato a 32 bit in formato big-endian | 1.0.2 |
4 | 4 byte | Float: un numero in virgola mobile IEEE 754 a 32 bit a precisione singola | 1.0.2 |
5 | 8 byte | Long: un numero a due complementi firmato a 64 bit in formato big-endian (prende due slot nella tabella delle costanti) | 1.0.2 |
6 | 8 byte | Double: un numero in virgola mobile IEEE 754 a 64 bit a doppia precisione (occupa due slot nella tabella delle costanti) | 1.0.2 |
7 | 2 byte | Riferimento alla classe: un indice all’interno del pool di costanti per una stringa UTF-8 contenente il nome completo della classe (in formato interno) (big-endian) | 1.0.2 |
8 | 2 byte | Riferimento a una stringa UTF-8 (anche big-endian) | 1.0.2 |
9 | 4 byte | Riferimento al campo: due indici all’interno del pool di costanti, il primo che punta a un riferimento alla classe, il secondo a un descrittore di nome e tipo. (big-endian) | 1.0.2 |
10 | 4 byte | Riferimento al metodo: due indici all’interno del pool di costanti, il primo che punta a un riferimento di classe, il secondo a un descrittore di nome e tipo. (big-endian) | 1.0.2 |
11 | 4 byte | Riferimento al metodo dell’interfaccia: due indici all’interno del pool di costanti, il primo che punta a un riferimento alla classe, il secondo a un descrittore di nome e tipo. (big-endian) | 1.0.2 |
12 | 4 byte | Nome e descrittore del tipo: due indici a stringhe UTF-8 all’interno del pool di costanti, il primo rappresentante un nome (identificatore) e il secondo un descrittore del tipo appositamente codificato. | 1.0.2 |
15 | 3 byte | Method handle: questa struttura è usata per rappresentare un method handle e consiste in un byte di type descriptor, seguito da un indice all’interno del constant pool. | 7 |
16 | 2 byte | Tipo di metodo: questa struttura è usata per rappresentare un tipo di metodo e consiste in un indice all’interno del pool di costanti. | 7 |
17 | 4 byte | Dynamic: è usata per specificare una costante calcolata dinamicamente prodotta dall’invocazione di un metodo bootstrap. | 11 |
18 | 4 byte | InvokeDynamic: è usato da un’istruzione invokedynamic per specificare un metodo bootstrap, il nome dell’invocazione dinamica, i tipi di argomento e ritorno della chiamata, e opzionalmente, una sequenza di costanti aggiuntive chiamate argomenti statici al metodo bootstrap. | 7 |
19 | 2 byte | Modulo: è usato per identificare un modulo. | 9 |
20 | 2 byte | Pacchetto: è usato per identificare un pacchetto esportato o aperto da un modulo. | 9 |
Ci sono solo due tipi di costante integrale, integer e long. Altri tipi integrali che appaiono nel linguaggio di alto livello, come boolean, byte e short devono essere rappresentati come una costante intera.
I nomi delle classi in Java, quando sono completamente qualificati, sono tradizionalmente separati da punti, come “java.lang.Object”. Tuttavia, all’interno delle costanti di riferimento alle classi di basso livello, appare una forma interna che utilizza invece le barre, come “java/lang/Object”.
Le stringhe Unicode, nonostante il moniker “UTF-8 string”, non sono effettivamente codificate secondo lo standard Unicode, sebbene sia simile. Ci sono due differenze (vedi UTF-8 per una discussione completa). La prima è che il punto di codice U+0000 è codificato come sequenza di due byte C0 80
(in esadecimale) invece della codifica standard a un byte 00
. La seconda differenza è che i caratteri supplementari (quelli fuori dal BMP a U+10000 e oltre) sono codificati usando una costruzione a coppie surrogate simile a UTF-16 piuttosto che essere codificati direttamente usando UTF-8. In questo caso ciascuno dei due surrogati è codificato separatamente in UTF-8. Per esempio, U+1D11E è codificato come la sequenza a 6 byte ED A0 B4 ED B4 9E
, piuttosto che la corretta codifica UTF-8 a 4 byte di F0 9D 84 9E
.
.