SectionsEdit
Existem 10 seções básicas para a estrutura do arquivo de classe Java:
- Magic Number: 0xCAFEBABE
- Version of Class File Format: as versões menor e maior do arquivo de classe
- Constant Pool: Pool de constantes para a classe
- Bandeiras de Acesso: por exemplo, se a classe é abstrata, estática, etc.
- Esta Classe: O nome da classe atual
- Super Classe: O nome da super classe
- Interfaces: Quaisquer interfaces na classe
- Campos: Quaisquer campos da classe
- Métodos: Quaisquer métodos na classe
- Atributos: Quaisquer atributos da classe (por exemplo, o nome do arquivo fonte, etc.)
Magic NumberEdit
Class files são identificados pelo seguinte cabeçalho de 4 bytes (em hexadecimal): CA FE BA BE
(as 4 primeiras entradas na tabela abaixo). A história deste número mágico foi explicada por James Gosling referindo-se a um restaurante em Palo Alto:
“Costumávamos ir almoçar a um lugar chamado St Michael’s Alley. De acordo com a lenda local, no passado profundamente escuro, os Grateful Dead costumavam actuar lá antes de o tornarem grande. Era um lugar bem divertido que era definitivamente um Grateful Dead Kinda Place. Quando Jerry morreu, eles até colocaram um pequeno santuário budista. Quando íamos lá, referíamo-nos ao lugar como Cafe Dead. Em algum lugar ao longo da linha, notou-se que este era um número HEX. Eu estava reformulando algum código de formato de arquivo e precisava de um par de números mágicos: um para o arquivo objeto persistente e outro para as classes. Eu usei CAFEDEAD para o formato de arquivo objeto, e no grepping para palavras hexadecimais de 4 caracteres que cabiam depois de “CAFE” (parecia ser um bom tema) eu cliquei em BABE e decidi usá-lo. Naquela época, não parecia ser muito importante ou destinado a ir a lugar algum, a não ser a lata de lixo da história. Então CAFEBABE tornou-se o formato de arquivo de classe, e CAFEDEAD era o formato de objeto persistente. Mas o recurso de objeto persistente foi embora, e junto com ele foi o uso do CAFEDEAD – ele foi eventualmente substituído pelo RMI.
Layout geralEdit
Porque o arquivo de classe contém itens de tamanho variável e não contém também offsets de arquivo embutido (ou ponteiros), ele é tipicamente parsed sequencialmente, do primeiro byte para o final. No nível mais baixo o formato do arquivo é descrito em termos de alguns tipos de dados fundamentais:
- u1: um inteiro não assinado de 8 bits
- u2: um inteiro não assinado de 16 bits em ordem de byte big-endian
- u4: um inteiro não assinado de 32 bits em ordem de byte big-endian
- tabela: um array de itens de comprimento variável de algum tipo. O número de itens na tabela é identificado por um número de contagem anterior (a contagem é um u2), mas o tamanho em bytes da tabela só pode ser determinado examinando cada um de seus itens.
Alguns desses tipos fundamentais são então reinterpretados como valores de nível superior (como strings ou números de ponto flutuante), dependendo do contexto.
byte offset | tamanho | tipo ou valor | descrição |
---|---|---|---|
0 | 4 bytes | u1 = 0xCA hex |
número mágico (CAFEBABE) usado para identificar arquivo como estando em conformidade com o formato de arquivo de classe |
1 | u1 = 0xFE hex |
||
2 | u1 = 0xBA hex |
||
3 | u1 = 0xBE hex |
||
4 | 2 bytes | u2 | minor número da versão do formato de arquivo de classe sendo usado |
5 | |||
6 | 2 bytes | u2 | maior número de versão do formato de arquivo de classe sendo usado. Java SE 17 = 61 (0x3D hex), Java SE 16 = 60 (0x3C hex), Java SE 15 = 59 (0x3B hex), Java SE 14 = 58 (0x3A hex), Java SE 13 = 57 (0x39 hex), Java SE 12 = 56 (0x38 hex), Java SE 11 = 55 (0x37 hex), Java SE 10 = 54 (0x36 hex), Java SE 9 = 53 (0x35 hex), Java SE 8 = 52 (0x34 hex), Java SE 7 = 51 (0x33 hex), Java SE 6.0 = 50 (0x32 hex), Java SE 5.0 = 49 (0x31 hex), JDK 1,4 = 48 (0x30 hex), JDK 1,3 = 47 (0x2F hex), JDK 1,2 = 46 (0x2E hex), JDK 1,1 = 45 (0x2D hex). Para detalhes dos números das versões anteriores ver nota de rodapé 1 em The JavaTM Virtual Machine Specification 2nd edition |
7 | |||
8 | 2 bytes | u2 | contagem de pool constante, número de entradas na seguinte tabela de pool constante. Esta contagem é pelo menos uma maior que o número real de entradas; veja a seguinte discussão. |
9 | |||
10 | cpsize (variável) | tabela | tabela de pool constante, um conjunto de entradas de pool constante de tamanho variável, contendo itens como números literais, strings e referências a classes ou métodos. Indexada a partir de 1, contendo (contagem constante do pool – 1) número de entradas no total (ver nota). |
… | |||
… | |||
… | |||
10+tamanho | 2 bytes | u2 | bandeira de acesso, a bitmask |
11+cpsize | |||
12+cpsize | 2 bytes | u2 | identifica esta classe, indexar na piscina constante a uma entrada do tipo “Classe” |
13+cpsize | |||
14+cpsize | 2 bytes | u2 | identifica a super classe, indexar na piscina constante a uma entrada do tipo “Classe” |
15+tamanho | |||
16+tamanho | 2 bytes | u2 | contagem de interfaces, número de entradas na seguinte tabela de interface |
17+tamanho | |||
18+tamanho | isize (variável) | tabela | tabela de interface: uma matriz de índices de comprimento variável de pool constante descrevendo as interfaces implementadas por esta classe |
… | |||
… | |||
….. | |||
18+tamanho+tamanho | 2 bytes | u2 | contagem de campos, número de entradas na seguinte tabela de campos |
19+cpsize+isize | |||
20+cpsize+isize | fsize (variável) | table | field table, array de campos de comprimento variável
cada elemento é uma estrutura field_info definida em https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 |
… | |||
… | |||
….. | |||
20+tamanho+tamanho+tamanho+tamanho | 2 bytes | u2 | contagem do método, número de entradas na seguinte tabela de métodos |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize (variável) | tabela | tabela de métodos, array de métodos de comprimento variável
cada elemento é um method_info structure definido em https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6 |
… | |||
… | |||
…<..> | |||
22+tamanho+tamanho+tamanho+tamanho+msize | 2 bytes | u2 | contagem de atributos, número de entradas na seguinte tabela de atributos |
23+cpsize+isize+isize+fsize+msize | |||
24+cpsize+isize+isize+fsize+msize | asize (variável) | table | attribute table, array de atributos de comprimento variável
cada elemento é uma estrutura de atributos_info definida em https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7 |
… | |||
… | |||
… |
Representação em uma linguagem de programação tipo CEditar
Desde que C não suporta múltiplas matrizes de comprimento de variáveis dentro de uma estrutura, o código abaixo não irá compilar e serve apenas como uma demonstração.
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;}
A constante poolEdit
A tabela constante pool é onde a maioria dos valores literais das constantes são armazenados. Isto inclui valores como números de todos os tipos, strings, nomes identificadores, referências a classes e métodos, e descritores de tipo. Todos os índices, ou referências, a constantes específicas na tabela de pool de constantes são dados por números de 16 bits (tipo u2), onde o valor do índice 1 se refere à primeira constante da tabela (o valor do índice 0 é inválido).
De acordo com as escolhas históricas feitas durante o desenvolvimento do formato de arquivo, o número de constantes na tabela de pool de constantes não é na verdade o mesmo que a contagem do pool de constantes que precede a tabela. Primeiro, a tabela é indexada começando em 1 (ao invés de 0), mas a contagem deve na verdade ser interpretada como o índice máximo mais um. Além disso, dois tipos de constantes (longas e duplas) ocupam dois espaços consecutivos na tabela, embora o segundo seja um índice fantasma que nunca é usado diretamente.
O tipo de cada item (constante) no pool constante é identificado por uma tag de byte inicial. O número de bytes que seguem esta tag e sua interpretação dependem do valor da tag. Os tipos de constantes válidas e seus valores de tag são:
Borbyte de tag | Borbytes adicionais | Descrição da constante | Versão introduzida |
---|---|---|---|
1 | 2+x bytes (variável) |
UTF-8 (Unicode) string: uma cadeia de caracteres prefixada por um número de 16 bits (tipo u2) indicando o número de bytes na cadeia codificada que se segue imediatamente (que pode ser diferente do número de caracteres). Note que a codificação utilizada não é realmente UTF-8, mas envolve uma pequena modificação do formulário de codificação padrão Unicode. | 1.0.2 |
3 | 4 bytes | Integer: um número de complemento de 32 bits em formato big-endian | 1.0.2 |
4 | 4 bytes | Float: um número de ponto flutuante IEEE 754 de 32 bits de precisão única | 1.0.2 |
5 | 8 bytes | Long: um número de complemento assinado de 64 bits de dois no formato big-endian (leva duas ranhuras na tabela constante do pool) | 1.0.2 |
6 | 8 bytes | Duplo: um número de ponto flutuante IEEE 754 de 64 bits de dupla precisão (leva duas ranhuras na tabela de pool constante) | 1.0.2 |
7 | 2 bytes | Referência de classe: um índice dentro do pool constante para uma string UTF-8 contendo o nome da classe totalmente qualificado (em formato interno) (big-endian) | 1.0.2 |
8 | 2 bytes | Referência de corda: um índice dentro do pool constante para uma string UTF-8 (big-endian também) | 1.0.2 |
9 | 4 bytes | Referência de campo: dois índices dentro do pool de constantes, o primeiro apontando para uma referência de Classe, o segundo para um descritor de Nome e Tipo. (big-endian) | 1.0.2 |
10 | 4 bytes | Referência de Método: dois índices dentro do pool constante, o primeiro apontando para uma referência de Classe, o segundo para um descritor de Nome e Tipo. (big-endian) | 1.0.2 |
11 | 4 bytes | Referência do método de interface: dois índices dentro do pool de constantes, o primeiro apontando para uma referência de Classe, o segundo para um descritor de Nome e Tipo. (big-endian) | 1.0.2 |
12 | 4 bytes | Nome e descritor de tipo: dois índices para cadeias de caracteres UTF-8 dentro do pool de constantes, o primeiro representando um nome (identificador) e o segundo um descritor de tipo especialmente codificado. | 1.0.2 |
15 | 3 bytes | Manipulação do método: esta estrutura é usada para representar uma manipulação de método e consiste em um byte de descritor de tipo, seguido por um índice dentro do pool de constantes. | 7 |
16 | 2 bytes | Tipo de método: esta estrutura é usada para representar um tipo de método, e consiste em um índice dentro do pool de constantes. | 7 |
17 | 4 bytes | Dinâmica: esta é usada para especificar uma constante dinamicamente calculada produzida por invocação de um método bootstrap. | 11 |
18 | 4 bytes | InvokeDynamic: é usado por uma instrução invokedynamic para especificar um método bootstrap, o nome da invocação dinâmica, o argumento e os tipos de retorno da chamada, e opcionalmente, uma sequência de constantes adicionais chamadas de argumentos estáticos para o método bootstrap. | 7 |
19 | 2 bytes | Módulo: este é usado para identificar um módulo. | 9 |
20 | 2 bytes | Package: é usado para identificar uma embalagem exportada ou aberta por um módulo. | 9 |
Existem apenas dois tipos de constantes integrais, inteiro e longo. Outros tipos integrais que aparecem na linguagem de alto nível, tais como boolean, byte e short devem ser representados como uma constante inteira.
Nomes de classes em Java, quando totalmente qualificados, são tradicionalmente separados por pontos, tais como “java.lang.Object”. Contudo dentro das constantes de referência de classe de baixo nível, aparece uma forma interna que utiliza slashes, como “java/lang/Object”.
As strings Unicode, apesar do moniker “UTF-8 string”, não são na verdade codificadas de acordo com o padrão Unicode, embora seja semelhante. Existem duas diferenças (ver UTF-8 para uma discussão completa). A primeira é que o ponto de código U+0000 é codificado como a sequência de dois bytes C0 80
(em hexadecimal) em vez da codificação padrão de um byte 00
. A segunda diferença é que caracteres suplementares (aqueles fora do BMP em U+10000 e acima) são codificados usando uma construção de par substituto similar ao UTF-16 ao invés de serem diretamente codificados usando UTF-8. Neste caso, cada um dos dois substitutos é codificado separadamente em UTF-8. Por exemplo, U+1D11E é codificado como a sequência de 6 bytes ED A0 B4 ED B4 9E
, em vez da codificação correcta de 4 bytes UTF-8 de F0 9D 84 9E
.
.