classファイルの中身
Javaって書いたことあるけど、.classってどんな感じになっているのか知らなかったので、ちょっといじってみました。
javapで逆アセ
.classの逆アセンブルはjavapコマンドを利用すると可能です。
class Foo {
}
のようなFoo.javaをjavac Foo.java
すると
> javap -c Foo Compiled from "Foo.java" class Foo { Foo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return }
のように命令列を見ることが可能です。
Jasminでアセンブラ
Jasminを使うと、aload_0とかinvokespecialのような命令を直接書いて.class
ファイルにコンパイルすることができます。
インストールやHelloWorldはJavaアセンブラ「Jasmin」を使おう [Javaプログラミング] All Aboutが参考になりました。
JVMの具体的な命令セットに関しては、 Java仮想マシン - Wikipediaに解説があります。
また、Jasmin Examples にはオブジェクトのインスタンスを生成したり、継承関係をどう表現するかなどの実践的な内容があります。
Classfile Verify
JVMではclassファイルを実行する前に、そのclassファイルが安全かどうかをある程度チェックする機構があります。たとえば定義していない変数が参照されていないかどうか、やスタックが膨れ上がってスタックオーバーフローを引き起こさないかどうかをチェックしてくれます。
これによってjasmin経由で生成したclassファイルでもある程度安全に動作することが担保できます。
最初はjavaのコンパイラを通らないようなやばいコードをアセンブラで書いて、ヤバい感じにjvmをぶっこわしてやるぜー₍₍ (ง ˘ω˘ )ว ⁾⁾と思っていたのですが世の中はそんなに甘くないですね(´・_・`)
詳しくはこちらのスライドの47p以降が参考になります。
www.slideshare.net
例
色々と参考にしましたが、何故か2引数のメソッドを作る例がなかったりしたので、 デモを増やそうと思い、簡単なサンプルを作成しました。
コマンドライン引数から計算方法
引数1
引数2
を指定すると計算してくれる電卓アプリです。
> java ExeCalculator add 1 2 3 > java ExeCalculator mul 1 2 2
コードはこちら。
二引数メソッドの例ですが、
.method public add(II)I ~~~ .end method
とすると
public int add(int a, int b) { ~~~ }
となります。
カンソウ(⁰⊖⁰)
JVMはスタックマシンなので複数レジスタを管理しなければならない通常のアセンブラよりも面倒かなと思ったのですが、 逆にどのレジスタに値が入っているかを管理しなくて済み、シンプルに治まるケースもあって書きやすかったです。
goto
を乱用すると先述したclassfile verify
機構に怒られてしまう(同じpc
なのにスタックサイズが変わってしまうケースがある。)のでinvokevirtual
でのインスタンスメソッドやinvokestatic
でのスタティックメソッド呼び出しを使うことになりますが、せっかくアセンブリ書くならなるべく使いたくないなと思い、ExeCalculator
のほうは冗長に書いてみました。
文字列比較に==を使って爆死していたのは秘密です。
メソッドごとにlocal変数の数やstackサイズを指定するのですが面倒臭かったので適当です。
こういうのはコンパイラが自動でやってくれ〜(∩´﹏`∩)という気持ちになりましたが、
普通はコンパイラが自動でやってくれているので今後は日々感謝の気持ちを持ちながらjavac
したいと思いました。(´・_・`)