2013年2月7日木曜日

[Clojure]clojure.asmでHello World

Clojureはバイトコードを作成するために ASM というライブラリ(Javaバイトコード操作用フレームワーク)を 利用しているようです。

(JVMの)Clojureを利用できる環境では(たぶん)ASMが利用できる(clojure.asm)ので、 勉強がてらこのライブラリを利用してHelloWorldプログラムを作成してみます。

(import '[clojure.asm ClassWriter Opcodes])

(def target-name "Hello")

(def cw (ClassWriter. ClassWriter/COMPUTE_MAXS))

;; public class Hello extends java.lang.Object { ...
(.visit cw
        Opcodes/V1_5       ; バージョン
        Opcodes/ACC_PUBLIC ; アクセス修飾子
        target-name        ; クラス名
        nil                ; シグネチャ
        "java/lang/Object" ; 親クラス
        nil)               ; インターフェース名の配列

;; MethodWriter
;; public static void main(java.lang.String []){ ...
(def mw (.visitMethod cw
                      (bit-or Opcodes/ACC_PUBLIC
                              Opcodes/ACC_STATIC) ; アクセス修飾子
                      "main"                      ; メソッド名
                      "([Ljava/lang/String;)V"    ; ディスクリプタ(引数と戻り値の型)
                      nil                         ; シグネチャ
                      nil))                       ; 例外名の配列

;; メソッドの内容
(.visitCode mw)
(.visitFieldInsn mw
                 Opcodes/GETSTATIC
                 "java/lang/System"
                 "out"
                 "Ljava/io/PrintStream;")
(.visitLdcInsn mw "Hello World!")
(.visitMethodInsn mw
                  Opcodes/INVOKEVIRTUAL
                  "java/io/PrintStream"
                  "println"
                  "(Ljava/lang/String;)V")
(.visitInsn mw Opcodes/RETURN)
(.visitMaxs mw 0 0)
(.visitEnd mw)

(.visitEnd cw)

;; バイトコードを取得
(def bytecode (.toByteArray cw))

;; classファイルを作成
(with-open [out (java.io.FileOutputStream. (str target-name ".class"))]
  (.write out bytecode))

上記のコードを実行すると Hello.class という名前でクラスファイルが作成されるので、 javaコマンドで実行してみます。

> java Hello
Hello World!

javapコマンドでバイトコードを逆アセンブルしてみると、 Clojureで書いたコードと対応してるように見えます。

javap -c Hello.class 
public class Hello {
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #14                 // String Hello World!
       5: invokevirtual #20                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}

Clojureのコンパイル時の動作を詳しく知りたい場合、このライブラリを理解しておいたほうが良さそうです。

0 件のコメント:

コメントを投稿