gen-class とバイトコード @tnoda_ 第 3 回 gen-class 勉強会 [2015-05-12 Tue]
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_06 1: invokespecial #1 // Method
java/lang/Object."<init >":()V7 4: return89 public long inc(long);
10 Code:11 0: lload_112 1: lconst_113 2: ladd14 3: lreturn15 }
単純なクラスの例 (gen-class)
Example (bar.clj)
1 (gen-class2 :name Bar3 :methods [[inc [long] long ]])45 (defn- -inc ^long6 [^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 }
Example (javap Foo)
1 Compiled from "Foo.java"2 class Foo {3 Foo();4 public long inc(long);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 }
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-class2 :name Bar3 :methods [[inc [long] long ]])
身に覚えなの無いメソッド (2/2)
1 user > (.clone (Bar.))2 CloneNotSupportedException Bar java.lang.Object.clone (
Object.java:-2)34 user > (. hashCode (Bar.))5 133240721067 user > (. toString (Bar.))8 "Bar@4ce58390"9
10 user > (. equals (Bar.) (Bar.))11 false
gen-classの仕様clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of thenon-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
Specifies the superclass, the non-private methods ofwhich will be overridden by the class. If not provided,defaults to Object.
スーパークラスが :extends で明示的に指定されない場合のデフォルトは Object.
gen-classの仕様clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of thenon-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
Specifies the superclass, the non-private methods ofwhich will be overridden by the class. If not provided,defaults to Object.
スーパークラスが :extends で明示的に指定されない場合のデフォルトは Object.
gen-classの仕様clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of thenon-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
Specifies the superclass, the non-private methods ofwhich will be overridden by the class. If not provided,defaults to Object.
スーパークラスが :extends で明示的に指定されない場合のデフォルトは Object.
gen-classの仕様clone() がつくられるのは gen-class の仕様
docstring (:methods)
The generated class automatically defines all of thenon-private methods of its superclasses/interfaces.
生成クラスにはスーパークラスおよび実装インタフェースのプラ
イベードではないメソッドが自動的に定義される。
docstring (:extends)
Specifies the superclass, the non-private methods ofwhich 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: dup4: invokevirtual #66 // Method clojure/lang/Var.isBound:()Z7: ifeq 16
10: invokevirtual #69 // Method clojure/lang/Var.get:()Ljava/lang/Object;13: goto 1816: pop17: aconst_null18: dup19: ifnull 3422: checkcast #53 // class clojure/lang/IFn25: aload_026: invokeinterface #57, 2 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;)Ljava/lang/Object;31: goto 3934: pop35: aload_036: invokespecial #71 // Method java/lang/Object.clone:()Ljava/lang/Object;39: areturn
clone()の実装 (2/2)
1 clone_var スタティックフィールドの Var を確認
2 バインドされていないか null ならば Object.clone() を呼び出し
3 バインドされていれば IFn にキャストして Clojure の関数を実行 (IFn.invoke())
継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
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: dup4: invokevirtual #66 // Method clojure/lang/Var.isBound:()Z7: ifeq 16
10: invokevirtual #69 // Method clojure/lang/Var.get:()Ljava/lang/Object;13: goto 1816: pop17: aconst_null18: dup19: ifnull 4422: checkcast #53 // class clojure/lang/IFn25: aload_026: lload_127: invokestatic #106 // Method clojure/lang/Numbers.num:(J)Ljava/lang/Number;30: invokeinterface #91, 3 // InterfaceMethod clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;35: checkcast #75 // class java/lang/Number38: invokevirtual #110 // Method java/lang/Number.longValue:()J41: goto 5544: pop45: new #112 // class java/lang/UnsupportedOperationException48: dup49: ldc #114 // String inc (example/-inc not defined?)51: invokespecial #117 // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V54: athrow55: lreturn
inc()の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならばUnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を実行 (IFn.invoke())
実際の処理は Clojure の関数 -inc が実行するので、バイトコードには処理の内容が含まれていない!
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
inc()の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならばUnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を実行 (IFn.invoke())
実際の処理は Clojure の関数 -inc が実行するので、バイトコードには処理の内容が含まれていない!
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
inc()の実装 (2/2)
1 inc_var スタティックフィールドの Var を確認
2 バインドされていないか null ならばUnsupportedOperationException
3 バインドされていれば IFn にキャストして Clojure の関数を実行 (IFn.invoke())
実際の処理は Clojure の関数 -inc が実行するので、バイトコードには処理の内容が含まれていない!
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Checkpoint 1gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Clojure 関数を呼び出すだけのプロキシ
Checkpoint 1gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Clojure 関数を呼び出すだけのプロキシ
Checkpoint 1gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Clojure 関数を呼び出すだけのプロキシ
Checkpoint 1gen-class が生成するメソッド実装
継承メソッドのデフォルト実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければスーパークラスのメソッドを呼び出す
3 対応する Clojure 関数があれば、その関数を実行
非継承メソッドの実装
1 対応する Clojure 関数があるかどうかを確認
2 無ければ UnsupportedOperationException
3 対応する Clojure 関数があれば、その関数を実行
Clojure 関数を呼び出すだけのプロキシ
残された疑問
疑問 1
1 clone_var スタティックフィールドの Var を確認
疑問 2
1 inc_var スタティックフィールドの Var を確認
残された疑問
疑問 1
1 clone_var スタティックフィールドの Var を確認
疑問 2
1 inc_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");
67 static {8 RT.var("clojure.core", "load"). invoke("/example");9 }
Static Initializer (3/3)
Example (Java)
1 Var.internPrivate("example", "-inc");
Example (Clojure)
1 example /-inc
スタティックフィールドの中身
関数(メソッドの処理を担当)の Var をキャッシュ
Static Initializer (3/3)
Example (Java)
1 Var.internPrivate("example", "-inc");
Example (Clojure)
1 example /-inc
スタティックフィールドの中身
関数(メソッドの処理を担当)の Var をキャッシュ
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 で生成した .class ファイルの変更なし
システムの動的な変更 (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との違い
• 動的 に変更できるかどうか
動的とは
システムを 動 かしたまま変更できるかどうか
• Java . . . 全身麻酔• Clojure . . . 意識のある状態で脳外科手術
Checkpoint 3
Javaの classと Clojureの gen-classとの違い
• 動的 に変更できるかどうか
動的とは
システムを 動 かしたまま変更できるかどうか
• Java . . . 全身麻酔• Clojure . . . 意識のある状態で脳外科手術
Wrap-up
gen-classが生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ• Var 経由でメソッドと Clojure 関数とをリンク
gen-classで Javaクラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Clojure の動的さを Java の世界に持ち込む魔法
Wrap-up
gen-classが生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ• Var 経由でメソッドと Clojure 関数とをリンク
gen-classで Javaクラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Clojure の動的さを Java の世界に持ち込む魔法
Wrap-up
gen-classが生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ• Var 経由でメソッドと Clojure 関数とをリンク
gen-classで Javaクラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Clojure の動的さを Java の世界に持ち込む魔法
Wrap-up
gen-classが生成するクラスファイルの特徴
• 実際の処理を Clojure 関数に委譲するプロキシ• Var 経由でメソッドと Clojure 関数とをリンク
gen-classで Javaクラスをつくる利点
• システムを動的に変更できる
• gen-class 生成クラスファイルのコンパイル不要
Clojure の動的さを Java の世界に持ち込む魔法