OpenJDKのJavaのコンパイラは標準ライブラリのシンボルデータを別途持っていて --release でビルドした時はそのデータが使われるようだ

Spotbugsのビルドを弄ろうとしていて気になったので調べてみた。

以下のように、--releaseと-source/-target を使った時で挙動が違う。

f:id:reteria:20201002050301p:plain

まずはじめにSpotbugsの話になるが、Spotbugsにはツールの特性上、テストケースとしてJavaファイルを中に溜め込んでいる。 また、その中には削除されてしまった標準ライブラリのAPIに関するテストケースがあり 古いJavaでビルドする必要のあるファイルもある。

その中で今回あったのが以下のようなクラスだ。

class RunFinalizersOnExit {

    int f() {
        System.runFinalizersOnExit(true);
        return 42;
    }

}

System#runFinalizersOnExit は Java11で削除されたメソッドだ。 そのため、このクラスはJava8の標準ライブラリが必要になるはずだ

しかし、--release を使った場合、そうはならない。 なぜならタイトルにも書いたとおり、OpenJDKのJavacコンパイラは、内部に古いバージョンのシンボルを溜め込んでいて 古いターゲットが指定された時は、そのシンボルファイルを使うようだ。

このディレクトリにシンボルが入っていてjava.base-8.sym.txtなどがあり、このあたりのファイルに書かれているシンボルが読まれているのだと思われる。

使っているJVMに付属する標準ライブラリのシンボルのみが解決できるものだと思っていたので非常に驚いた。 終わり。

追記:

この辺で、シンボルを保存したバイナリを生成していて $JAVA_HOME/jmods/jdk.compiler.jmod の中の lib/ct.sym というところに格納されているようだ。 (jmodファイルはただのzipです)