Java の class ファイルのバージョンを調べる Ruby スクリプト

Java の .class ファイルのバージョンを確認する必要があったので
さくっと Ruby スクリプトを書いた。


[class-version-checker.rb]

#!ruby

ARGV.each { |path|
  open(path, "rb") { |file|
    a = file.read(10)
    p "#{a[6]*0xff+a[7]}.#{a[4]*0xff+a[5]}"
  }
}

実行例

$ ruby class-version-checker.rb target.class
"49.0"


こういう細かいスクリプトをサクっと書けるのが
Ruby のいいところだなぁとうっとりしていたら
file コマンドで取得できるが後からわかってがっかりしたという話。

$ file target.class
target.class: compiled Java class data, version 49.0

がっかりしたので

version 49.0 (Java 1.5)の命令セットと
version 48.0 (Java 1.4)の命令セットは同じものだと聞いたことがあったので
バージョン情報だけ書き換えれば
Java 1.5 でコンパイルした class ファイルが Java 1.4 で
動かせるとも思ったので実験した。


[Target.java]

public class Target {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}


[class-version-changer.rb]

#!ruby

ARGV.each { |path|
  open(path, "rb") { |file|
    a = file.read
    if a[7] == 49 then
      a[7] = 48
      open(path + ".new", "wb").write a
      puts "path" + ".new"
    end
  }
}


まずは Java 1.5 でコンパイル

$ javac -version
javac 1.5.0_11
...
$ javac Target.java


Java 1.5 で実行

$ java -version
java version "1.5.0_11"
...
$ java Target
Hello World


Java 1.4 で実行

$ java -version
java version "1.4.2_14"
$ java Target
Exception in thread "main" java.lang.UnsupportedClassVersionError: Target (Unsupported major.minor version 49.0)
...


スクリプトで変換後 Java 1.4 で実行

$ ruby class-version-changer.rb Target.class
Target.class.new
$ mv Target.class Target.class.old
$ mv Target.class.new Target.class
$ java Target
Hello World


ホントに動いちゃった。
試したプログラムは簡単な Hello World プログラムで
複雑なプログラムでどうなるかはわからないので注意。