2016-01-19 10 views
6

私はJetbrainsのFernFlowerのa forkに取り組んでいます。JVMバイトコード、ローカル変数のタイプはどのようにして見つけることができますか?

FernFlowerについて私を本当に悩ますことの1つは、bpush/spushなどの値に基づいてローカル変数の型を決定することです.JodeとProcyonは何とかローカル変数の元の値を見つける方法を見つけます。

ここに元のソースコードがあります。

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); 
} 

FernFlowerを逆コンパイルすると、それはこの出力:

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); 
} 

をしかしJODE /プロキオンで逆コンパイルするとき、それは、元のローカル変数の型を出力:

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); 
    } 

私がどのように思っていましたコンパイル時にローカル変数型情報が格納されていないと思ったのでこれが可能ですか? FernFlowerに同じ機能を追加するにはどうしたらいいですか?

+0

逆コンパイラがどのローカルタイプを使用するかを決めることは可能でしょうか?(例: 'byte'対' int')? –

+3

多くの場合、それらのスロットを使用する手順から推論できるはずです。バイトコードを自分自身で見てください。特に、呼び出される 'String.valueOf()'のさまざまなオーバーロードがあります。この場合、デバッグ情報もあるように見えます。それ以外の場合は、その名前も利用できませんでした。 – EJP

+0

デバッグ情報がなければ、コンパイルされたコードにはLocalVariableTableが存在しないので、元の宣言された型がどのように推定されるかわかりません。

答えて

2

周辺を見て、何らかの理由でFernFlowerがLocalVariableTableのデータのいくつかを完全に無視することを決めたのを発見しました。

@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(); 
    } 
} 

それは今、適切にJODEと同じコードを出力します。あなたは以下を追加する必要が

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(); 
    } 
} 

あなたが情報を入力したい場合:ここで

はLocalVariableTable復号するためのシダ元のコードです変数型:)

FernFlowerがこの情報を無視する理由を知りました。

+0

このコードは変数のスコープを完全に無視していますか?これは、最も単純なコード例以外は何のためにもうまくいかないでしょう... – Holger

+0

あなたの応答をもう少し拡張できますか?私はバイトコードを初めて使う人です。 –

+0

'start'と' end'は、変数が有効なバイトコード内の範囲を表します。この範囲外では、別の変数には同じ「varIndex」番号が使用される可能性があります。名前は異なる名前とタイプを持つ可能性があります。マップキーとして 'varIndex'を使用しているので、最後に見つかった変数のみを各インデックスに記録します。コードを 'for(int ix = 0; ix <10; ix ++){/ * loop body * /}(String str:list){/ *別のループ本体* /}'と考えてください。ここで、 'ix'と' str'は分離したスコープを持ち、バイトコードで同じvarインデックスを使うことができます。 – Holger

4

.classファイルには、オプションでデバッグ用の「LocalVariableTable」属性が含まれています。コマンドjavap -l <Class>.classを呼び出すと、データがある場合はそのデータを見ることができます。

関連する問題