アルゴリズムとデータ構造1

コンパイラ
2012年11月8日
酒居敬一@A468([email protected])
http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html
1
例外処理
プログラムの実行中に発生した、例外的な事象を取り扱
うこと。


例外的な事象に対処するために、大域的なジャンプにより、
例外処理プログラムに処理を移す。
例外的な事象として、例えば次のようなもの。




スタックのオーバーフロー
セグメンテーション違反・nullポインタ例外
0除算
例外処理の実装について。




2
プログラミング言語
OS
プロセッサ
原始的な実装
OSが存在していれば、OSの例外処理機構を利用する。



各プロセスにシグナルとして実装されている。
例外の発生により、対応するシグナルハンドラが呼ばれる。


例外発生によりOSに処理が戻るとともにプロセスは停止し、
シグナルハンドラからプロセスの実行を再開する。
OSの機能なので、システムコールで使える。

プロセッサが検出でき、OSが受けた例外の一部しか対応できない。
プロセッサの例外処理機構を利用する。


例外の発生により、発生源に固有のアドレスへジャンプする。


高級言語からの言語仕様の範囲内での利用は不可能。

3
たいていは、最低限のレジスタはスタックに退避してあるので、
原因を除去できれば復帰できる。
言語拡張かアセンブリ記述を使うしかない。
例外処理の例(110ページ)
1. class Example {
2.
public static void main(String[] args){
3.
try{
4.
method1(args);
5.
System.out.println("This Line is not executed.");
6.
}
7.
catch(Exception e){ // Exception (かその派生クラス)に関する例外ハンドラ
8.
System.out.println(e.getMessage());
9.
}
10.
}
11.
static void method1(Object o){
12.
synchronized(o){
13.
method2();
14.
System.out.println("This Line is not executed.");
15.
}
16.
}
17.
staic void method2(){
18.
throw (new Exception("Jump To Handler."));
// 例外発生源
19.
}
20. }
4
例外処理の実行手順
例外発生により、すぐに大域的ジャンプが発生するのではない。
{20行目で例外が生成され投げられたので、20行目を検査すると try ブロッ
クの中にはない。よって、} methdo2() から抜けて呼び出し元に戻る。
{呼び出し元の14行目を検査すると、14行目が try ブロックの中にはない。
ただし、synchronized ブロックの中にあるので、いきなり呼び出し元にに戻
るわけにはいかない。13行目で synchronized ブロックに突入する際に、指
定した変数 o が参照するインスタンスの排他制御権を確保したので、例外
処理とはいえども、確保した排他制御権を synchronized ブロックから抜ける
ときに開放するべきである。よって、} 呼び出し元の14行目に着いたら排他
制御権を開放し、続いて呼び出し元に戻る。
{呼び出し元の4行目を検査すると、try ブロックの中にあり、後続のcatch
節は7行目にある。よって、}呼び出し元の4行目に着いたら catch 節のある
7行目にジャンプし、投げられた例外が7行目の catch 節において捕捉すべ
きインスタンスか検査する。結果として捕捉すべき例外でることから、8行目
にある例外処理を行う。

1.
2.
3.
5
例外処理のコンパイル法
コンパイル時に実行できる部分



中括弧で囲んだ部分。
try ブロックや synchronized ブロックの中にあるかどうか
は、構文解析すればわかる。

コンパイル時にできる検査はあらかじめ済ませておき、実行時に
しか実行できないコードを出力する。
実行時にしか実行できない部分。



中括弧の外側。
戻る・捕捉するといった分岐・条件分岐。


インスタンスの実行時の型の検査。

6
プログラマが思うところで、例外処理できる。
プログラマが例外を自由に設計できる。
例外処理のコード生成
ソースコード中の例外の発生源となりうる場所について、
取るべき動作を決める。


発生源としては throw の他にメソッド呼び出しも考慮する。


呼び出した先から捕捉されなかった例外が伝播してくる。
例外発生場所に基づき、取るべき動作を決定する。



try ブロックの中では、対応する catch の検査・分岐。
synchronized ブロックの中では、排他制御権の開放。
メソッドから呼び出し元に例外処理として戻る。
決めた動作に基づいてコードを生成する。


メソッドから戻ったとき、通常処理か例外処理の区別。

7
通常通り戻るか、例外を捕捉・伝播する。
2返戻値法による実装
メソッドの返戻値を2つとする方法。



メソッドの定義で明示した戻り値。
例外発生フラグ。

メソッド呼び出し直後に、フラグを調べ、対応する例外処理を行う。


例外の伝播: メソッドから抜けて呼び出し元に戻る。
例外の捕捉: 対応する catch 節にジャンプする。
通常処理と例外処理のフローが一時重なることが問題


呼び出し直後でフラグでフローを分離する処理コスト。


8
通常処理フローの中から例外処理を分離する非合理さ。
そもそも、例外処理を通常処理フローに合流しなければ発生しない。
説明用プログラムの共通定義
例外を処理する関数へのポインタを定義。
発生した例外に対応する処理関数への参照を
そのポインタに代入することで実装する。


2値返戻法では、メソッド(関数)の実行ごとに検査。
表引き法では、戻り番地から例外処理を引き出す。


1.
2.
3.
4.
5.
6.
7.
8.
struct ExecEnv { // スレッド固有の資源を収める構造体
…
void *exception;
// 例外発生フラグ。発生した例外への参照
…
};
#define exceptionOccurred(ee) ((ee)->exception != NULL)
#define excpetionThrow(ee, obj) ((ee)->exception = obj)
#define exceptionClear(ee) ((ee)->exception = NULL)
9
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
void Example8_main(ExecEnv *ee, void *args){
Example8_method1(ee, args);
if(exceptionOccured(ee)){
goto CATCH;
}
println(ee, System.out, "This Line is not executed.");
if(exceptionOccured(ee)){
goto CATCH;
}
NORMAL_EXIT:
return;
CATCH:
{
void *object = ee->exception;
if(is_instance_of(object, Exception, ee)){
exceptionClear(ee);
println(ee, System.out, getMessage(ee, object));
if(exceptionOccured(ee)){
goto EXCEPION_EXIT;
}
goto NORMAL_EXIT;
}
}
EXCEPTION_EXIT:
return;
}
10
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
void Example8_method1(ExecEnv *ee, void *o){
EnterSynchronizedBlock(o);
if(exceptionOccured(ee)){
goto EXCEPTION_EXIT;
}
Example_method2(ee);
if(exceptionOccured(ee)){
goto SYNCHRONIZED_BLOCK_EXCEPTION_EXIT;
}
println(ee, System.out, "This Line is not executed.");
if(exceptionOccured(ee)){
goto SYNCHRONIZED_BLOCK_EXCEPTION_EXIT;
}
ExitSynchronizedBlock(o);
if(exceptionOccured(ee)){
61. void Example8_method2(ExecEnv *ee){
goto EXCEPTION_EXIT;
62.
void *object = newInstance(ee, Exception);
}
63.
if(exceptionOccured(ee)){
return;
64.
goto EXCEPTION_EXIT;
SYNCHRONIZED_BLOCK_EXCEPTION_EXIT;
65.
}
ExitSynchronizedBlock(o);
66.
Exception_init(ee, object, "Jump to Handler.");
EXCEPTION_EXIT:
67.
if(exceptionOccured(ee)){
return;
68.
goto EXCEPTION_EXIT;
}
69.
}
70.
exceptionThrow(ee, object);
71. EXCEPTION_EXIT:
72.
return;
73. }
11
表引き法による実装

メソッドからの戻りを例外の場合はreturnで処理しない。


フラグで分離した先へ goto でもってメソッドから戻る。
例外発生のときの戻り番地をあらかじめ知る方法を用意。

12
通常の戻り先をキーに、例外処理の分岐先を取り出す。
戻り番地
例外発生時の戻り番地
3
6
4
6
12
15
21
27
22
25
23
25
24
25
24
27
27
27
33
36
34
36
プログラム中のどこから来たか?
という情報よりも
どこへ戻るのか?
という情報を使う。
呼ばれた関数の
スタックフレーム
戻り番地
それは、戻り番地は暗黙的に渡されているため、
特別に処理しなくても獲得できる情報だから。
ee
args
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
void Example8_main(ExecEnv *ee, void *args){
Example8_method1(ee, args);
println(ee, System.out, "This Line is not executed.");
NORMAL_EXIT:
return;
CATCH:
{
void *object = ee->exception;
if(is_instance_of(object, Exception, ee)){
exceptionClear(ee);
println(ee, System.out, getMessage(ee, object));
goto NORMAL_EXIT;
}
}
EXCEPTION_EXIT:
goto (lookUpReturnAddressInException(returnAddress());
}
呼んだ関数の
スタックフレーム
13
← TOS
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
void Example8_method1(ExecEnv *ee, void *o){
EnterSynchronizedBlock(o);
Example_method2(ee);
println(ee, System.out, "This Line is not executed.");
ExitSynchronizedBlock(o);
return;
SYNCHRONIZED_BLOCK_EXCEPTION_EXIT;
ExitSynchronizedBlock(o);
EXCEPTION_EXIT:
goto (lookUpReturnAddressInException(returnAddress());
}
void Example8_method2(ExecEnv *ee){
void *object = newInstance(ee, Exception);
Exception_init(ee, object, "Jump to Handler.");
exceptionThrow(ee, object);
EXCEPTION_EXIT:
goto (lookUpReturnAddressInException(returnAddress());
}
14
Null Pointer Exception

実行時例外のいくらかはプロセッサが検出できる






セグメンテーション違反(IA-32では、一般保護例外)
ページ不在例外
0除算例外
バスエラー(IA-32では、一般保護例外)
プロセッサが検出できるものについては、検査コードを
生成しないで、シグナルなどを経由してプロセッサから
例外を受け取る。
null pointer は特定のアドレス値に設定してある。


15
その番地を{コード|データ}セグメントに入れないでおく。
ページ管理表で、その番地を含むページを不在状態にしておく。
[Intel, ``Software Developer Manual’’]
ページングとセグメンテーションは組み合わされることが多い