Learn · concepts

Cos'è il bytecode della JVM? Spiegazione dei file Java

Il bytecode della JVM è il set di istruzioni compatto che la Java Virtual Machine esegue. Scopri come funzionano i file .class, perché i mod riscrivono il bytecode e dove si colloca ASM.

TRtrolPublished 5 min read

Cos'è il bytecode della JVM?

Il bytecode della JVM è l'insieme di istruzioni che la Java Virtual Machine esegue. Quando si compila il codice sorgente Java, il compilatore non produce codice macchina per la propria CPU. Produce bytecode, un formato compatto e portabile memorizzato nei file .class. La JVM legge tali istruzioni a runtime e le converte in codice nativo al momento dell'esecuzione.

Questo approccio a strati—dal codice sorgente al bytecode e poi al codice macchina—è ciò che rende Java portabile. Lo stesso file .class viene eseguito su Windows, macOS, Linux o qualsiasi altro sistema dotato di una JVM compatibile, perché la JVM è lo strato che sa come eseguire le istruzioni sulla macchina locale. Scrivi una volta, esegui ovunque.

Come il codice sorgente diventa bytecode

Un file .java è un testo scritto da una persona. Un file .class è l'output compilato che la JVM esegue. Il compilatore legge il codice sorgente, lo controlla e scrive un file .class per ogni classe, contenente istruzioni bytecode più un pool di costanti, tabelle dei metodi e altre metadati.

Il bytecode non è legato a un processore specifico. Lo stesso file .class viene eseguito su Windows, macOS, Linux o su qualsiasi sistema dotato di un runtime Java, perché la JVM è lo strato che sa come eseguire le istruzioni sulla macchina locale. Questa portabilità è il punto focale del formato.

Come appaiono le istruzioni

Il bytecode è un insieme di istruzioni basato su uno stack. La maggior parte delle operazioni spinge valori su uno stack di operandi, li estrae e spinge un risultato. Non c'è allocazione di registri da considerare; il modello è deliberatamente semplice in modo che la JVM possa verificarlo ed eseguirlo rapidamente.

Alcuni esempi di opcode danno un'idea:

OpcodeCosa fa
iloadSpinge una variabile locale int sullo stack
iaddEstrae due int, spinge la loro somma
invokevirtualChiama un metodo di istanza
getfieldLegge un campo di istanza
returnRitorna da un metodo

Raramente li leggi o li scrivi a mano. Gli strumenti li generano e li consumano, ed è qui che entra in gioco una libreria come ASM.

Perché il bytecode è importante per i mod

Il bytecode è importante perché permette di modificare il comportamento senza il codice sorgente originale. Un mod di Minecraft quasi mai ha il codice sorgente del gioco. Ciò che ha è il gioco compilato su disco. Per aggiungere o alterare una funzionalità, un mod legge il bytecode esistente, lo riscrive e invia il risultato alla JVM mentre le classi vengono caricate.

Questo è come i framework di patching inseriscono hook nei metodi che non possiedono. Trovano il punto giusto in un metodo compilato e inseriscono una chiamata al codice del mod. Lavorare a livello di bytecode significa che un mod può collegarsi al gioco anche senza vedere una riga del codice Java originale.

Nella pratica, è così che Terminus e altri client basati su Fabric modificano Minecraft. Invece di disporre del codice sorgente di Minecraft, lavorano con i file .class compilati e riscrivono il bytecode per aggiungere funzionalità come moduli, schermate delle impostazioni e rendering personalizzato.

Dove ASM entra in gioco

ASM è una libreria Java per la lettura, la scrittura e la trasformazione del bytecode. Analizza un file .class in eventi o in un albero, consente di modificare le istruzioni ed emette un file .class valido. È piccolo, veloce e opera a stretto contatto con il formato grezzo, motivo per cui gli strumenti di modding di livello superiore si basano su di esso anziché analizzare direttamente i file di classe.

La maggior parte dei modder non chiama mai ASM direttamente. Utilizzano un livello superiore che nasconde gli opcodi ed espone un modo più intuitivo per dire "esegui il mio codice all'inizio di questo metodo". Sotto la superficie, quel livello modifica il bytecode, spesso tramite ASM.

Decompilazione del bytecode al codice sorgente

Decompilatori come Fernflower e CFR prendono i file .class compilati e ricostruiscono codice Java leggibile a partire dal bytecode. Il processo non è perfetto: alcuni nomi e commenti vengono persi, alcune strutture sono più difficili da recuperare rispetto ad altre, e il codice offuscato diventa illeggibile. Ma per codice ordinario e non offuscato, i decompilatori di solito producono un output sufficientemente vicino all'originale da poterlo comprendere e utilizzare.

Questo è utile per capire come funziona il gioco, ma non sostituisce il possesso del codice sorgente effettivo. Il codice decompilato è comunque logica compilata senza commenti, e i nomi delle variabili ricostruiti possono essere fuorvianti. È uno strumento di lettura, non di scrittura.

FAQ