致命的なエラーの対処法
エラーの種類
Duktapeが扱うエラーは3種類あります。
- throw、duk_error()、duk_throw()などで発生する通常のエラー。
- 捕捉されないエラー、duk_fatal()の明示的な呼び出し、あるいは Duktape内部での回復不能なエラーによって引き起こされる致命的なエラー。
- Duktapeのヒープやスレッドのコンテキストを持たない致命的なエラーで、 Duktape内部のアサーションに失敗した場合などに発生します。
通常のエラーは、longjmpやC++の例外を使用して内部的に伝播されます(設定に依存します)。これらのエラーはECMAScriptのtry-catchやprotected C APIコールを使って捕捉されます。
捕捉されないエラーなどによる致命的なエラーは、duk_create_heap() で登録された致命的なエラーハンドラの呼び出しをトリガーします。handler 引数に NULL が指定された場合、組み込みのデフォルトの致命的エラーハンドラが代わりに使用されます。デフォルトの致命的なエラーハンドラは、DUK_USE_CPP_EXCEPTIONS 設定オプションに応じて、 abort() を呼び出すか、C++ 例外 (duk_fatal_exception) を投げるかのどちらかです。カスタムの致命的なエラーハンドラを提供することが強く推奨されます。
コンテキストがない致命的なエラーは、現在アサーション失敗に限定されていますが、コンテキストがないとヒープ関連のハンドラを検索できないので、常に組み込みのデフォルトの致命的なエラーハンドラの呼び出しをトリガーします。これらは、明示的にアサーションを有効にした場合のみ発生します。
致命的なエラーハンドラの例
ヒープ作成時にハンドラを登録する。
duk_context *ctx;
void *my_udata = (void *) 0xdeadbeef; /* 最も有用なものは、NULLである。 */
ctx = duk_create_heap(NULL, NULL, NULL, my_udata, my_fatal);
/* ... */
致命的なエラーハンドラは、例えば、次のようなものがある。
static void my_fatal(void *udata, const char *msg) {
(void) udata; /* この場合、サイレント警告は無視されます */
/* 'msg' は NULL でもよいことに注意すること。 */
fprintf(stderr, "*** FATAL ERROR: %s\n", (msg ? msg : "no message"));
fflush(stderr);
abort();
}
Duktape 1.xでは、致命的なエラーハンドラ関数にエラーコードの引数が追加されていました。
デフォルトの致命的なエラーハンドラを内蔵
組み込みのデフォルトの致命的なエラーハンドラは、基礎となるプラットフォームに関する最小限の仮定に対して最適化されています。デフォルトの動作は DUK_USE_CPP_EXCEPTIONS コンフィグオプションに依存します。
DUK_USE_CPP_EXCEPTIONS が無効な場合 (デフォルト)、 デフォルトのハンドラはデバッグログエントリを書き (ただし stdout や stderr には何も書きません!)、 DUK_ABORT() 設定定義を通じて abort() を呼び出します (これは置き換え可能です)。abort() が返されると、ハンドラは無限ループに入り、致命的なエラーの後に実行が続かないことを確認します (これは明らかに決して起こってはいけません)。
DUK_USE_CPP_EXCEPTIONS が有効な場合: デフォルトのハンドラは、デバッグログエントリを書き (stdout や stderr には何も書きません)、duk_fatal_exception を投げます。この例外は std::runtime_error を継承しており、致命的なエラーメッセージにアクセスするための ::what() メソッドを提供します。この例外は捕捉可能ですが、捕捉した後に実行を継続することは安全ではありません。
Duktape 2.3以前では、DUK_USE_CPP_EXCEPTIONSの動作は少し異なっていました。キャッチできないエラーはduk_internal_exceptionとして伝播し、他の致命的なエラー(アサーション失敗など)はabort()を使用します。
デフォルトの動作はほとんどの環境ではあまり有用ではないので、そうすべきです。
1.ヒープを作成するときに致命的なエラーハンドラを提供する。これは良い習慣です。なぜなら、致命的なエラー(アサーション失敗を除く)がどのように処理されるかを制御できるからです。 2.これは、明示的な致命的エラー・ハンドラを持たないDuktapeヒープや、 コンテキストなしで引き起こされる致命的エラー(アサーション・エラーのような) の致命的エラー処理を改善するものです。デフォルト・ハンドラをオーバーライドすることは、Duktapeをシステム・ ライブラリとして提供する場合に特に重要です。 Example of overriding the default fatal error handler in duk_config.h:
/* stdio.h' が含まれていることを確認する。 */
#define DUK_USE_FATAL_HANDLER(udata,msg) do { \
const char *fatal_msg = (msg); /* 二重評価を避ける */ \
(void) udata; \
fprintf(stderr, "*** FATAL ERROR: %s\n", fatal_msg ? fatal_msg : "no message"); \
fflush(stderr); \
abort(); \
} while (0)
また、genconfig --option-file のようにすると、ハンドラを指定することができます。
# my_fatal.yaml
DUK_USE_FATAL_HANDLER:
verbatim: |
#define DUK_USE_FATAL_HANDLER(udata,msg) do { \
const char *fatal_msg = (msg); /* 二重評価を避ける */ \
(void) udata; \
fprintf(stderr, "*** FATAL ERROR: %s\n", fatal_msg ? fatal_msg : "no message"); \
fflush(stderr); \
abort(); \
} while (0)