Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

HDDエラーで喪失したjavaソースコードをjd-gui活用で復活

Javaソースへの逆変換のことを書いていたら、以前にHDDエラーで喪失したJavaソースコードを、保管されていたjarから復活できることに気が付きましたので、この機会に復活しました。忘備録として復活方法を書いておきます。

ここでの復活方法は、次のような応用が可能です。例えば、あるバージョンのjarファイルと、別のバージョンjarファイルのみが残っていたとき、これらの差分を取って修正内容を把握し、更に新しいバージョンのjarファイルに適用したい場合とかに、使えます。202HW(ポータブルTV)のフル画面化 追補 - Itsukaraの日記にも関係します。

経緯

2010年1月に、数独に興味を持ち、DIUF(Department of Informatics at the University of Fribourg)のNicolas Juillerさんが作成した数独解法プログラム(Sudoku Explainer)をhttp://diuf.unifr.ch/pai/people/juillera/Sudoku/Sudoku.htmlからダウンロードして、色々と試して楽しんでいました。

使っているうちに、数独を解く手順を、途中まで戻したいと感じました。そこで、Nicolas Juillerさんが書いたJavaソースプログラムを修正して、手順を戻す機能"Undo Step"を追加しました。"Undo Step"は、初期配置に戻るまで何回も適用可能です。

その後、HDDエラーで、ソースコードは失われたのですが、実行用のjarファイルは、オリジナルのものも、機能追加したものも残っていました。これがあれば、使うことができるので、そのままにしていましたが、ソースも復活させたいとは思っていました。ただ、最近まで忘れていました。

復活方法

Javaコンパイラjd-guiの起動

オリジナル版jarを逆コンパイル

  • jd-guiの画面に、オリジナル版jarファイルをドラッグ&ドロップ
  • jd-guiのFileメニューから"Save All Sources"を選択し、逆コンパイル後の全Sourceファイルを含むzipファイルを保存
  • 上記で保存されたzipファイルを解凍
  • 解凍で作成されたディレクトリは複製を作っておく(後でディレクトリ内のファイル修正してから、修正前のものを参照する可能性があるため)

機能追加版jarを逆コンパイル

  • (オリジナル版jarと同様に実施)

javaソース中の不要部分削除

  • コンパイルしたjavaファイルの先頭に、下記のような行番号情報が付いているので、これを削除する(差分を取るときに邪魔になるため)
/*     */   private boolean isWorth(Hint hint)
/*     */   {
/*  77 */     if (!this.isFiltered)
/*  78 */       return true;
/*     */     boolean isWorth;
/*  80 */     boolean isWorth; if ((hint instanceof DirectHint)) {
/*  81 */       isWorth = isWorth(this.givenCells, (DirectHint)hint);
/*     */     } else
/*  83 */       isWorth = isWorth(this.removedPotentials, this.givenCells, (IndirectHint)hint);
/*  84 */     return isWorth;
/*     */   }
  • 具体的には、サクラエディッタの検索メニューの"Grep置換機能"を使い、逆コンパイルしたjavaソースファイルを含むディレクトリ中の全てのjavaファイルで、先頭の行番号情報を削除
    • 置換前:^/\*[ \d]+\*/
    • 置換後:
    • サブフォルダからも検索する:(チェックする)
    • 正規表現:(チェックする)
  • 下記の2つの行も、差分で邪魔になるので、空行で置き換える
/* Location:              D:\Develop\Sudoku Explainer\SudokuExplainer.jar!\diuf\sudoku\gui\SudokuExplainer.class
 * Java compiler version: 5 (49.0)

差分の作成

  • 例えば次のコマンドで差分を作成(Cygwin64環境で実施しましたが、Linuxなど、色々な環境で実施可能です)
diff -r -C 5 SudokuExplainer.jar.src SudokuExplainerPlus.jar.src > diffC5.txt
  • 上記コマンドの補足:
    • "-r"は、Recuresiveにディレクトリ内をすべて比較することを指示
    • "-C 5"は、差分のContextとして前後5行を出力に含めることを指示
      • (Contextがあった方が、目で見て場所を探す場合に役立つため)

オリジナルソースの修正

  • 差分を見ながら、オリジナルソースを修正する
    • 修正するファイル名は、差分中に出力されたdiffコマンドに記載されている
    • 修正場所は、修正部分のContextと、差分中に書かれた行数を見て推測
      • 差分中の行数は、逆コンパイル後の行数であり、参考程度の情報
      • 正確な修正場所を知りたい場合は、サクラエディッタで行番号情報を削除する前のjavaファイルを見る
    • コンパイルしたJavaソースファイルは、元々のJavaソースファイルと少し異なる表現になっているところもあるので、適宜対応する
      • メソッド中のメンバへのアクセスに必ず"this."が付いている(不要)
      • 値が定数のものは、メンバ名ではなく定数に置き換わっている

機能追加版のjavaソースをコンパイルして確認

  • 以上により、機能追加版のjavaソースができるので、Eclipseなどの環境にインポートして、エラーがないかチェックし、必要に応じ修正
  • 必要に応じて、実行して試す

オリジナル版との差分

  • オリジナル版(v1.2.1)との差分、および、機能追加版の実行ファイル(jar)を下記に掲載しました。ご参考まで。

GitHub - Itsukara/SudokuExplainerPlus

あとがき

忘備録ですが、もしかしたら、何らかの役立つかもしれませんので、一応、書きました。ご参考まで。