gen-class とバイトコード
@tnoda_
第 3 回 gen-class 勉強会
[2015-05-12 Tue]
Outline
gen-class のバイトコード
• メソッド
• クラスの初期化
Java の class との違い
• バイトコードの違い
• gen-class にしかできないこと
単純なクラスの例 (Java)
Example (Foo.java)
1 class Foo {
2 public long inc(long x) {
3 return x + 1;
4 }
5 }
javap の実行例 (1/2)
Example (javap Foo)
1 Compiled from "Foo.java"
2 class Foo {
3 Foo();
4 public long inc(long);
5 }
javap の実行例 (2/2)
Example (javap -c Foo)
1 Compiled from "Foo.java"
2 class Foo {
3 Foo();
4 Code:
5 0: aload_0
6 1: invokespecial #1 // Method
java/lang/Object."<init >":()V
7 4: return
8
9 public long inc(long);
10 Code:
11 0: lload_1
12 1: lconst_1
13 2: ladd
14 3: lreturn
15 }
単純なクラスの例 (gen-class)
Example (bar.clj)
1 (gen-class
2 :name Bar
3 :methods [[inc [long] long ]])
4
5 (defn- -inc ^long
6 [^long x]
7 (inc x))
Example (Foo.java)
1 class Foo {
2 public long inc(long x) {
3 return x + 1;
4 }
5 }
javap の実行例
Example (javap Bar)
1 public class Bar {
2 public static {};
3 public Bar();
4 public java.lang.Object
clone ();
5 public int hashCode ();
6 public java.lang.String
toString ();
7 public boolean equals(java
.lang.Object );
8 public long inc(long);
9 }
javap の実行例
Example (javap Bar)
1 public class Bar {
2 public static {};
3 public Bar();
4 public java.lang.Object
clone ();
5 public int hashCode ();
6 public java.lang.String
toString ();
7 public boolean equals(java
.lang.Object );
8 public long inc(long);
9 }
Example (javap Foo)
1 Compiled from "Foo.java"
2 class Foo {
3 Foo();
4 public long inc(long);
5 }
身に覚えの無いメソッド (1/2)
_人人人人人人人人人人人人人人_
>  clone();  <
>  hashCode();  <
>  toString();  <
>  equals(java.lang.Object);  <
 ̄YYYYYYYYYYYYYY ̄
Example (bar.clj)
1 (gen-class
2 :name Bar
3 :methods [[inc [long] long ]])
身に覚えなの無いメソッド (2/2)
1 user > (.clone (Bar.))
2 CloneNotSupportedException Bar java.lang.Object.clone (
Object.java:-2)
3
4 user > (. hashCode (Bar.))
5 1332407210
6
7 user > (. toString (Bar.))
8 "Bar@4ce58390"
9
10 user > (. equals (Bar.) (Bar.))
11 false
gen-class の仕様
clone() がつくられるのは gen-class の仕様
gen-class の仕様
clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of the
non-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
gen-class の仕様
clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of the
non-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
gen-class の仕様
clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of the
non-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
Specifies the superclass, the non-private methods of
which will be overridden by the class. If not provided,
defaults to Object.
スーパークラスが :extends で明示的に指定されない場合のデ
フォルトは Object.
スーパークラス由来のメソッド
_人人人人人人人人人人人人人人_
>  clone();  <
>  hashCode();  <
>  toString();  <
>  equals(java.lang.Object);  <
 ̄YYYYYYYYYYYYYY ̄
身に覚えの無いメソッドは Object 由来のもの
clone() の実装 (1/2)
public java.lang.Object clone();
Code:
0: getstatic #40 // Field clone__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #66 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #69 // Method clojure/lang/Var.get:()Ljava/lang/Obj
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 34
22: checkcast #53 // class clojure/lang/IFn
25: aload_0
26: invokeinterface #57, 2 // InterfaceMethod clojure/lang/IFn.invoke:(Lja
31: goto 39
34: pop
35: aload_0
36: invokespecial #71 // Method java/lang/Object.clone:()Ljava/lang/O
39: areturn
clone() の実装 (2/2)
1 clone_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば Object.clone() を呼
び出し
3 バインドされていれば IFn にキャストして Clojure の関数を
実行 (IFn.invoke())
clone() の実装 (2/2)
1 clone_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば Object.clone() を呼
び出し
3 バインドされていれば IFn にキャストして Clojure の関数を
実行 (IFn.invoke())
継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
inc() の実装 (1/2)
public long inc(long);
Code:
0: getstatic #24 // Field inc__var:Lclojure/lang/Var;
3: dup
4: invokevirtual #66 // Method clojure/lang/Var.isBound:()Z
7: ifeq 16
10: invokevirtual #69 // Method clojure/lang/Var.get:()Ljava/lang/Obj
13: goto 18
16: pop
17: aconst_null
18: dup
19: ifnull 44
22: checkcast #53 // class clojure/lang/IFn
25: aload_0
26: lload_1
27: invokestatic #106 // Method clojure/lang/Numbers.num:(J)Ljava/lan
30: invokeinterface #91, 3 // InterfaceMethod clojure/lang/IFn.invoke:(Lja
35: checkcast #75 // class java/lang/Number
38: invokevirtual #110 // Method java/lang/Number.longValue:()J
41: goto 55
44: pop
45: new #112 // class java/lang/UnsupportedOperationException
48: dup
49: ldc #114 // String inc (example/-inc not defined?)
inc() の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば
UnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を
実行 (IFn.invoke())
inc() の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば
UnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を
実行 (IFn.invoke())
実際の処理は Clojure の関数 -inc が実行するので、バイトコー
ドには処理の内容が含まれていない!
inc() の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば
UnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を
実行 (IFn.invoke())
実際の処理は Clojure の関数 -inc が実行するので、バイトコー
ドには処理の内容が含まれていない!
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Checkpoint 1
gen-class が生成するメソッド実装
Checkpoint 1
gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
Checkpoint 1
gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Checkpoint 1
gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Clojure 関数を呼び出すだけのプロキシ
残された疑問
残された疑問
疑問 1
1 clone_var スタティックフィールドの Var を確認
残された疑問
疑問 1
1 clone_var スタティックフィールドの Var を確認
疑問 2
1 inc_var スタティックフィールドの Var を確認
Static Initializer (1/3)
Figure : Bar クラスの static initializer
Static Initializer (2/3)
Example (Java)
1 private static final Var inc__var = Var.internPrivate("
example", "-inc");
2 private static final Var equals__var = Var.internPrivate
("example", "-equals");
3 private static final Var toString__var = Var.
internPrivate("example", "-toString");
4 private static final Var hashCode__var = Var.
internPrivate("example", "-hashCode");
5 private static final Var clone__var = Var.internPrivate(
"example", "-clone");
6
7 static {
8 RT.var("clojure.core", "load"). invoke("/example");
9 }
Static Initializer (3/3)
Example (Java)
1 Var.internPrivate("example", "-inc");
Static Initializer (3/3)
Example (Java)
1 Var.internPrivate("example", "-inc");
Example (Clojure)
1 example /-inc
Static Initializer (3/3)
Example (Java)
1 Var.internPrivate("example", "-inc");
Example (Clojure)
1 example /-inc
スタティックフィールドの中身
関数(メソッドの処理を担当)の Var をキャッシュ
Checkpoit 2
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
Figure : 初期化前
Checkpoit 2
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
Figure : スタティックフィールド初期化
Checkpoit 2
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
!"
Figure : 名前空間読み込み
Checkpoit 2
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
!"
Figure : メソッド呼び出し
Java のクラスとの違い
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
!"
Figure : gen-class のメソッド呼び出し
システムの動的な変更 (Java)
!"#$%&
'()*## '()*##
'+*,*
'+*,*
'()*##
手順
1 Java ソースを変更する
2 コンパイルする
3 Jar/class ファイルを置き換える
4 クラスローダでリロード/最悪再起動
システムの動的な変更 (Java)
!"#$%&
'()*## '()*##
'+*,*
'+*,*
'()*##
手順
1 Java ソースを変更する
2 コンパイルする
3 Jar/class ファイルを置き換える
4 クラスローダでリロード/最悪再起動
システムの動的な変更 (gen-class)
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
!"
手順
1 fn を更新して eval
システムの動的な変更 (gen-class)
!"#$%&"'' (&)*+#,
-.%//0"#
1#-0"2,3'2"2-%34"#
-.%5&).67
1+8&-%3&).6
,9":1&,;<-.%
!"
手順
1 fn を更新して eval
gen-class で生成した .class ファイルの変更なし
デモ
Figure : Minecraft
Checkpoint 3
Java の class と Clojure の gen-class との違い
Checkpoint 3
Java の class と Clojure の gen-class との違い
• 動的 に変更できるかどうか
動的とは
システムを 動 かしたまま変更できるかどうか
• Java . . . 全身麻酔
• Clojure . . . 意識のある状態で脳外科手術
Wrap-up
Wrap-up
gen-class が生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ
• Var 経由でメソッドと Clojure 関数とをリンク
Wrap-up
gen-class が生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ
• Var 経由でメソッドと Clojure 関数とをリンク
gen-class で Java クラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Wrap-up
gen-class が生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ
• Var 経由でメソッドと Clojure 関数とをリンク
gen-class で Java クラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Clojure の動的さを Java の世界に持ち込む魔法

gen-class とバイトコード(第3回 gen-class 勉強会資料)