2016-01-19 18 views
6

Sto lavorando su a fork di FernFlower di Jetbrains e ho aggiunto miglioramenti minori ad esso.Bytecode JVM, come posso trovare il tipo di variabili locali?

Una cosa che mi infastidisce davvero di FernFlower è che basa il tipo di variabile locale in base al suo valore in bpush/spush ecc. Mentre Jode e Procyon in qualche modo trovano un modo per trovare il valore originale di una variabile locale.

Ecco il codice sorgente originale.

public static void main(String[] args) throws Exception { 
    int hello = 100; 
    char a2 = 100; 
    short y1o = 100; 
    int hei = 100; 

    System.out.println(a2+" "+y1o+", "+hei+", "+hello); 
} 

Quando decompilato con FernFlower, esso Risulterà:

public static void main(String[] args) throws Exception { 
    byte hello = 100; 
    char a2 = 100; 
    byte y1o = 100; 
    byte hei = 100; 
    System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello); 
} 

Ma quando decompilato con Jode/Procyon Produce il originali tipi di variabili locali:

public static void main(String[] args) 
    throws Exception 
    { 
    int hello = 100; 
    char a2 = 'd'; 
    short y1o = 100; 
    byte hei = 100; 

    System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello); 
    } 

Mi chiedevo come è questo è possibile perché ho pensato che nessuna informazione di tipo variabile locale è stata archiviata al momento della compilazione? Come posso aggiungere la stessa funzionalità a FernFlower?

+0

È possibile che i decompilatori si siano presi delle libertà nel decidere quale tipo locale usare (ad esempio 'byte' contro' int')? –

+3

Dovresti essere in grado di dedurlo in molti casi dalle istruzioni che usano quegli slot. Dai un'occhiata al codice byte, in particolare i vari overload di 'String.valueOf()' che vengono chiamati. In questo caso sembra che ci fossero anche alcune informazioni di debug, altrimenti i nomi non sarebbero stati disponibili neanche. – EJP

+0

Senza informazioni di debug non ci sarebbe LocalVariableTable nel codice compilato, quindi non vedo come il tipo dichiarato originale possa essere dedotto.

risposta

2

Quindi, dopo aver guardato intorno e il debug, ho scoperto che per qualche ragione FernFlower decide di ignorare completamente alcuni dati in LocalVariableTable.

Ecco codice originale felci per decodificare il LocalVariableTable:

public void initContent(ConstantPool pool) throws IOException { 
    DataInputFullStream data = stream(); 

    int len = data.readUnsignedShort(); 
    if (len > 0) { 
     mapVarNames = new HashMap<Integer, String>(len); 
     for (int i = 0; i < len; i++) { 
      data.discard(4); 
      int nameIndex = data.readUnsignedShort(); 
      data.discard(2); 
      int varIndex = data.readUnsignedShort(); 
      mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString()); 
     } 
    } else { 
     mapVarNames = Collections.emptyMap(); 
    } 
} 

Se si desidera digitare le informazioni è necessario aggiungere il seguente:

@Override 
public void initContent(ConstantPool pool) throws IOException { 
    DataInputFullStream data = stream(); 

    int len = data.readUnsignedShort(); 
    if (len > 0) { 
     mapVarNames = new HashMap<Integer, String>(len); 
     mapVarTypes = new HashMap<Integer, String>(len); 
     for (int i = 0; i < len; i++) { 
      int start = data.readUnsignedShort(); 
      int end = start + data.readUnsignedShort(); 
      int nameIndex = data.readUnsignedShort(); 
      int typeIndex = data.readUnsignedShort(); 
      int varIndex = data.readUnsignedShort(); 
      mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString()); 
      mapVarTypes.put(varIndex, pool.getPrimitiveConstant(typeIndex).getString()); 
     } 
    } else { 
     mapVarNames = Collections.emptyMap(); 
     mapVarTypes = Collections.emptyMap(); 
    } 
} 

Ora emette lo stesso codice come Jode con una corretta tipi variabili :)

Mi chiedo perché FernFlower abbia scelto di ignorare questa informazione.

+0

Quindi questo codice sta ignorando completamente gli ambiti della variabile? È improbabile che funzioni per qualsiasi cosa tranne i più semplici esempi di codice ... – Holger

+0

Potresti ampliare la tua risposta un po 'di più? Sono nuovo di bytecode. –

+0

'start' e' end' descrivono un intervallo all'interno del bytecode, in cui la variabile è valida. Al di fuori di questo intervallo, è possibile utilizzare lo stesso numero 'varIndex' per una variabile diversa, che potrebbe avere un nome e un tipo diversi. Dato che stai usando 'varIndex' come chiave di mappa, registrerai solo l'ultima variabile incontrata per ogni indice. Pensa al codice come 'for (int ix = 0; ix <10; ix ++) {/ * corpo del loop * /} per (Str string: lista) {/ * altro corpo del ciclo * /}'.Qui, 'ix' e' str' hanno ambiti disgiunti e possono usare lo stesso indice var nel codice byte. – Holger

4

.class i file contengono facoltativamente un attributo "LocalVariableTable" ai fini del debug. Se invochi il comando javap -l <Class>.class puoi vedere i dati se sono presenti.

Problemi correlati