コンパイラの解析 (3) クラスとインスタンスの初期化.

Slides:



Advertisements
Similar presentations
アルゴリズムとプログラミン グ (Algorithms and Programming) 第6回:クラスとインスタンス クラスの宣言 アクセス修飾子 インスタンスの生成 (new キーワード) this キーワード フィールドとメソッドの実際の定義と使い 方 クラスの宣言 アクセス修飾子 インスタンスの生成.
Advertisements

独習JAVA Chapter 6 6.6 クラスの修飾子 6.7 変数の修飾子 結城 隆. 6.6 クラスの修飾 abstract インスタンス化できないクラス。1つまたは複数のサブクラスで 実装してはじめてインスタンス化できる。 final 継承されたくないことを明示する。これ以上機能拡張 / 変更でき.
6.4継承とメソッド 6.5継承とコンストラクタ 11月28日 時田 陽一
Applet 岡部 祐典 鈴木 敬幸.
~手続き指向からオブジェクト指向へ(Ⅰ)~
プログラミング基礎I(再) 山元進.
アルゴリズムとデータ構造1 2007年6月12日
アルゴリズムとプログラミング (Algorithms and Programming)
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
アルゴリズムとデータ構造 2011年6月13日
構造体.
アルゴリズムとプログラミング (Algorithms and Programming)
第2章 Eclipseと簡単なオブジェクト 指向プログラミング
第20章 Flyweight ~同じものを共有して無駄をなくす~
計算機プログラミングI 第8回 2002年12月5日(木) メソッドとクラス (教科書6章) クイズ インスタンスメソッド インスタンス変数
RMI ソフトウェア特論 第6回 /
第6回独習Javaゼミ 第6章 セクション4~6 発表者 直江 宗紀.
コンパイラの解析 (2) GCJのデータ構造 - 1.
実行時のメモリ構造(2) Javaスタック内動作他
オブジェクト指向 プログラミング 第十四回 知能情報学部 新田直也.
8.1 例外処理 8.2 catchブロックの検索 8.3 throwステートメント 8.4 例外とエラークラス 8.6 独自の例外
ソフトウェア工学 知能情報学部 新田直也.
プログラミング言語入門 手続き型言語としてのJava
アルゴリズムとプログラミング (Algorithms and Programming)
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
オブジェクト指向 プログラミング 第八回 知能情報学部 新田直也.
独習JAVA 6.8 コンストラクタの修飾子 6.9 メソッドの修飾子 6.10 ObjectクラスとClassクラス 11月28日(金)
オブジェクト指向 プログラミング 第十一回 知能情報学部 新田直也.
第7回独習Javaゼミ セクション 1~4 発表者 直江 宗紀.
オブジェクト指向 プログラミング 第十三回 知能情報学部 新田直也.
WebサービスII (第7回) 2007年11月7日 植田龍男.
プログラミング言語入門.
第11週:super/subクラス、継承性、メソッド再定義
Nakano School of Business 経営情報ビジネス科 【 Java概論(Test5)】
オブジェクト指向 プログラミング 第十四回 知能情報学部 新田直也.
プログラミング入門2 第11回 情報工学科 篠埜 功.
クラスファイルの構造解析(2) 2003年6月23日 海谷 治彦.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
オブジェクト指向 プログラミング 第十ニ回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
オブジェクト指向プログラミングと開発環境
new Calc(7,3).divInt()実行前
オブジェクト・プログラミング 第8回.
アルゴリズムとプログラミング (Algorithms and Programming)
オブジェクト指向 プログラミング 第九回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第八回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
プログラミング言語論 第十一回 理工学部 情報システム工学科 新田直也.
C#プログラミング実習 第3回.
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
計算機プログラミングI 第3回 プリミティブ値 クラスメソッド クラス変数 式と演算 変数の利用
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとプログラミング (Algorithms and Programming)
計算機プログラミングI 第4回 2002年10月31日(木) 問題解決とアルゴリズム クラスメソッドと手続きの抽象化 最大公約数
Chapter 5 5.5 thisキーワード 5.6 インスタンス変数とインスタンスメソッド 結城 隆
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
JAVA入門⑥ クラスとインスタンス.
オブジェクト指向言語論 第九回 知能情報学部 新田直也.
cp-2. 属性,アクセサ (C++ オブジェクト指向プログラミング入門)
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
情報処理Ⅱ 小テスト 2005年2月1日(火).
オブジェクト指向言語論 第九回 知能情報学部 新田直也.
オブジェクト指向言語論 第十回 知能情報学部 新田直也.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
計算機プログラミングI 第2回 2002年10月17日(木) 履習登録 複習 ライブラリの利用 (2.6-7) 式・値・代入 (2.6-8)
計算機プログラミングI 第10回 2002年12月19日(木) メソッドの再定義と動的結合 クイズ メソッドの再定義 (オーバーライド)
計算機プログラミングI 第5回 2002年11月7日(木) 配列: 沢山のデータをまとめたデータ どんなものか どうやって使うのか
Presentation transcript:

コンパイラの解析 (3) クラスとインスタンスの初期化

Table of Contents シンボル名 クラスの初期化 インスタンスの生成

シンボル名 JavaやC++のシンボルは名前マングルがかかった状態でオブジェクトファイルに格納される C言語のシンボルよりも多くの情報を含む 名前空間 引数情報

extern “C” extern “C” の記述は、マングルの抑制 extern “C” をしないとマングルされる extern "C" void sample_c(int a) {} void sample_cxx(int a) {} $ objdump -t mangle.o | grep sample 00000000 g F .text 00000005 sample_c 00000006 g F .text 00000005 _Z10sample_cxxi

名前デマングルツール binutilsのc++filtコマンドでデマングルできる 慣れれば脳内にフィルタを作れる? $ objdump -t mangle.o | grep sample | c++filt 00000000 g F .text 00000005 sample_c 00000006 g F .text 00000005 sample_cxx(int)

名前マングルの略式法則 フィールド メソッド <>一つ分で一つのシンボル/基本型を表す _Z<フィールド名>

名前マングル – シンボル 通常のシンボルは次の形式でマングル 例 <名前文字数><名前> a -> 1a sample -> 6sample

名前マングル – 名前空間つきシンボル 名前空間つきのシンボル 例 N<シンボル1><シンボル2>…E a::b -> N1a1bE java::lang::Math -> N4java4lang4MathE

名前マングル – 基本型 基本型は1文字にマングルされる メソッドのマングル時、引数に利用 int -> i double -> d void -> v … メソッドのマングル時、引数に利用 foo() は foo(void) と常に解釈

名前マングル – ポインタ ポインタは次の形式でマングル 例 P<シンボル> int * -> Pi int ** -> Pii java::lang::Object* -> PN4java4lang6ObjectE

名前マングル – テンプレート テンプレートは次の形式 例 I<シンボル1><シンボル2>…E JArray<int> -> 6JArrayIiE JArray<Hoge> -> 6JArrayIN4HogeEE

名前マングル – ここまでの例 フィールド メソッド java::lang::Object::class$ -> _ZN4java4lang6Object6class$E java::lang::Math::PI -> _ZN4java4lang4Math2PIE メソッド hoge::foo(int, java::lang::Object*) -> _ZN4hoge3fooEiPN4java4lang6ObjectE Sample::main(JArray<java::lang::String *>*) -> _ZN6Sample4mainEP6JArrayIPN4java4lang6StringEE

名前マングル – ここまでの例 (分解) Sample::main(JArray<java::lang::String *>*) _Z N 6Sample 4main E P 6JArray I P N 4java 4lang 6String E E

名前マングル – 省略形 同じ名前にでてくるシンボルは再利用できる hoge::Foo::b(int, hoge::Bar) S<数字>_ なし, 0, 1, … の順番で先頭から数字がつき、再利用可能 hoge::Foo::b(int, hoge::Bar) -> _ZN4hoge3Foo1bEiNS_3BarE hoge::Foo::a(int, hoge::Foo) -> _ZN4hoge3Foo1aEiS0_ foo::f(java::lang::Object*,java::lang::Object*) _ZN3foo1fEPN4java4lang6ObjectES3_

クラスの初期化 (1) ここの部分はどうコンパイルされる? 変数の初期化用コード public class StaticInit { public static double SQRT2 = Math.sqrt(2); }

クラスの初期化 (2) 逆アセンブルしてみる javap –c <class> $ javap -c StaticInit Compiled from "StaticInit.java“ .. static {}; Code: 0: ldc2_w #2; //double 2.0d 3: invokestatic #4; //Method java/lang/Math.sqrt:(D)D 6: putstatic #5; //Field SQRT2:D 9: return

クラス初期化子 (1) Static initializer (クラス初期化子) public class ClassInit { static { System.out.println("clinit"); } public static void main(String[] args) { System.out.println("main"); } }

クラス初期化子 (2) 実行すると、mainメソッドの前に呼び出される $ java ClassInit clinit main

クラス初期化子 (3) 誰がクラスの初期化を行うのか? mainが行っている形跡はない $ javap -c ClassInit Compiled from "ClassInit.java“ .. public static void main(java.lang.String[]); Code: 0: getstatic #2; // System.out 3: ldc #3; // "main“ 5: invokevirtual #4; // PrintStream.println(String) 8: return

クラス初期化のルール Java Virtual Machine Specification 2nd →Mainメソッドを呼び出す直前に初期化 次の場合、クラスTはVMによって初期化される Tの子クラスを初期化する直前 Tのインスタンスを生成する直前 Tのクラスメソッドを起動する直前 Tの定数でないクラス変数を参照する直前 同じクラスは1度しか初期化されない →Mainメソッドを呼び出す直前に初期化

一息 (1) Subを実行した結果は? class Circu { static final String S1 = Sub.S2; } class Sub extends Circu { static final String S2 = new String("Sub"); public static void main(String[] args) { System.out.println(Circu.S1); } }

一息 (2) トレースしてみる クラスメソッドの起動 →Subの初期化 class Circu { static final String S1 = Sub.S2; } class Sub extends Circu { static final String S2 = new String("Sub"); public static void main(String[] args) { System.out.println(Circu.S1); } } 子クラスの初期化 →Circuの初期化 S2は非定数 →初期化までnull S2は未初期化 →S1 == null

一息 (3) “null”と表示される Circu.S1初期化の時点でSub.S2が未初期化 $ java Sub null

閑話休題 gcjでは明示的にクラスの初期化をする gcjではコンパイル済みコードを実行 クラスの初期化方法を解析

GCJにおけるクラスの初期化 (1) 他のクラスを初期化するコードをコンパイル クラスメソッドの呼び出しはクラス初期化の原因 java.lang.Mathを初期化する public class GcjClinit { public static double sqrt(double d) { return Math.sqrt(d); }

GCJにおけるクラスの初期化 (2) コンパイルしたものを分析 $ gcj -S GcjClinit.java _ZN9GcjClinit4sqrtEd: .. (prologue) pushl $_ZN9GcjClinit6class$E call _Jv_InitClass .. call _ZN4java4lang4Math4sqrtEd .. (epilogue)

GCJにおけるクラスの初期化 (3) _Jv_InitClassとその引数について分析 $ echo '_ZN9GcjClinit6class$E' | c++filt GcjClinit::class$ $ objdump -T /usr/lib/libgcj.so.5 | grep _Jv_InitClass 06cc8632 w DF .text 0000002b Base _Jv_InitClass

GCJにおけるクラスの初期化 (4) CNIからクラスの初期化を行う実験 下記のクラスをCNIから初期化する public class ClassInit { static { System.out.println("clinit"); } public static void main(String[] args) { System.out.println("main"); } }

GCJにおけるクラスの初期化 (5) _Jv_InitClass(Class)を直接呼び出す Initializer::mainをCNIで作成 #include <stdio.h> #include "Initializer.h" extern "C" void *_ZN9ClassInit6class$E; extern "C" void _Jv_InitClass(void *); void Initializer::main(JArray<java::lang::String *> *) { puts("init >>"); _Jv_InitClass(&_ZN9ClassInit6class$E); puts("<< init"); }

GCJにおけるクラスの初期化 (6) コンパイルして実行 予想通りの場所に“clinit”の表示 $ gcj --main=Initializer ClassInit.java Initializer.java cni.cc $ ./a.out init >> clinit << init

GCJにおけるクラスの初期化 (7) _Jv_InitClassを使うとクラスを初期化できる 次のことが可能になった コンパイラのソースlibjava/prims.ccで定義 次のことが可能になった インスタンスの生成 クラスメソッドの呼び出し (定数でない) クラスフィールドの参照

インスタンスの生成 (1) 簡単なソースコードを書いて検証 コンパイルしてアセンブルファイルを読む public class New { int a, b; public New(int a, int b) { this.a = a; this.b = b; } public static void main(String[] args) { new New(123, 456); } }

インスタンスの生成 (2) _ZN3New4mainEP6JArrayIPN4java4lang6StringEE: .. pushl $_ZN3New6class$E call _Jv_InitClass .. pushl $4 pushl $_ZN3New6class$E call _Jv_AllocObjectNoFinalizer .. pushl $456 pushl $123 pushl %eax call _ZN3NewC1Eii …

インスタンスの生成 (3) インスタンスの生成は次の3ステップ クラスの初期化 インスタンス領域の割り当て コンストラクタの呼び出し _Jv_InitClass 引数にclass インスタンス領域の割り当て _Jv_AllocObjectNoFinalizer 引数にclass(, インスタンスのサイズ) コンストラクタの呼び出し _ZN3NewC1Eii -> New::New(int, int) 第一引数に割り当てたインスタンス

インスタンスの生成 (4) 3つのステップを忠実に再現 クラス初期化、メモリ割り当て、コンストラクタ呼出し #include "Initializer.h" ... void Initializer::main(JArray<java::lang::String *> *) { /* 1 */ _Jv_InitClass(&_ZN3New6class$E); /* 2 */ void *obj = _Jv_AllocObjectNoFinalizer( (java::lang::Class*)&_ZN3New6class$E, 4); /* 3 */ _ZN3NewC1Eii(obj, 123, 456); }

インスタンスの生成 (5) 成功したかどうか分からないので、フィールドの値を表示するようにしてみる

インスタンスの生成 (6) 構造を無理矢理たどって再現 #include <stdio.h> #include "Initializer.h“ ... void Initializer::main(JArray<java::lang::String *> *) { /* 1 */ _Jv_InitClass(&_ZN3New6class$E); /* 2 */ void *obj = _Jv_AllocObjectNoFinalizer( (java::lang::Class*)&_ZN3New6class$E, 4); /* 3 */ _ZN3NewC1Eii(obj, 123, 456); printf("a=%d, b=%d\n", ((int *)obj)[1], ((int *)obj)[2]); }

インスタンスの生成 (7) 実行してみる $ gcj --main=Initializer New.java Initializer.java cni.cc $ ./a.out a=123, b=456

次回 興味のあるところから ポリモーフィズムの実現 Javaの名前空間とオブジェクトファイルの名前空間 クラスの初期化 インスタンスの生成 クラスの登録 配列の扱い 例外の処理 synchronizeの処理 インスタンスの破棄 ガーベジコレクタとの調和