Compilatori e compilati (UPD)

Qualche tempo fa mi è successo uno sgradevole incidente: ho perso un’intera cartella piena di file .java!! Ovviamente, nonostante tutte le mie buone intenzioni, nessun backup recente!! 😀

Avendo a disposizione, però, ancora i file .class compilati, ho provveduto a decompilarli usando il programma jd (Java Decompiler). Il programma mi ha ripristinato quasi tutto il codice (non c’è riuscito con un solo metodo, che ho ripreso con un altro decompilatore), per qui alla fin fine ho perso solamente i commenti e la documentazione javadoc…

Quello che mi ha incuriosito è stato però come il codice sia stato trasformato dal compilatore, ovvero come alcuni costrutti sintattici del linguaggio comodi da utilizzare vengano poi trasformati in fase di compilazione, tra cui:

  • eliminazione dei generics: in particolare, in caso di estrazione da una collezione di un’oggetto, viene fatto un cast
  • nel caso di metodi con un numero variabile di argomenti, chiamando la funzione l’insieme dei parametri viene convertito automaticamente in un array di oggetti
  • alcune variabili final (ad esempio di tipo int) vengono sostituite nel codice con il loro valore (per cui me le sono dovute andare a ripescare)
  • nel caso di boxing e unboxing, vengono aggiunti i comandi per la creazione dell’oggetto (ad esempio new Integer(i)) e per estrarne il valore (ad esempio, .intValue(), che poi è quello che lancia un eventuale NullPointerException)
  • in caso di dichiarazioni multiple, me le ha separate (ad esempio “int a, b;” è stato cambiato in “int a; int b;“)
  • i cicli foreach me li ha tutti cambiati in for+iterator, e sono impazzito a trovare il modo di riottenerli come prima
  • la costruzione di una inner class è stata completamente stravolta: anche qui ho dovuto ricostruirla piano piano (peraltro, in questo caso, il codice decompilato non veniva più riconosciuto come valido dal compilatore…)

Oltre a questi trucchetti sintattici, il compilatore ha operato anche alcune ottimizzazioni, tra cui:

  • alcune sequenze di if/else concatenate sono state parzialmente riscotruite (ad esempio un “if{} else{ if{} else{}}” è stato cambiato in “if{} else if{} else{}“)
  • in caso di cicli con dentro una dichiarazione di variabile, la dichiarazione è stata spostata fuori dal ciclo (forse perché è più prestante riutilizzare lo stesso riferimento che non ricrearlo ogni volta), ad esempio un “while(){String a=...}” è stato cambiato in “String a; while(){a=...}
  • nei metodi con costrutti try/catch/finally, eventuali istruzioni di return sono state duplicate in ogni blocco

Insomma, una bella avventura che mi ha permesso di scoprire un po’ di cosette e fatto amare ancora di più i backup!! 😀

Aggiornamento 29/08/2014: se per caso ne avreste bisogno, il codice per istanziare una inner class del tipo

A.B obj = a.new B();

(con a oggetto già creato) viene trasformato in

A tmp135_134 = a;
tmp135_134.getClass();
A.B obj = new A.B(tmp135_134);

I numeri del riferimento temporaneo possono cambiare. 🙂

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *