hotspot/src/share/vm/interpreter/interpreterRuntime.cpp
// exception_handler_for_exception(...) returns the continuation address,
// the exception oop (via TLS) and sets the bci/bcp for the continuation.
// The exception oop is returned to make sure it is preserved over GC (it
// is only on the stack if the exception was thrown explicitly via athrow).
// During this operation, the expression stack contains the values for the
// bci where the exception happened. If the exception was propagated back
// from a call, the expression stack contains the values for the bci at the
// invoke w/o arguments (i.e., as if one were inside the call).
IRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* thread, oopDesc* exception))
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Handle h_exception(thread, exception);
methodHandle h_method (thread, method(thread));
constantPoolHandle h_constants(thread, h_method->constants());
typeArrayHandle h_extable (thread, h_method->exception_table());
bool should_repeat;
int handler_bci;
int current_bci = bci(thread);
{- -------------------------------------------
(1) #TODO
---------------------------------------- -}
// Need to do this check first since when _do_not_unlock_if_synchronized
// is set, we don't want to trigger any classloading which may make calls
// into java, or surprisingly find a matching exception handler for bci 0
// since at this moment the method hasn't been "officially" entered yet.
if (thread->do_not_unlock_if_synchronized()) {
ResourceMark rm;
assert(current_bci == 0, "bci isn't zero for do_not_unlock_if_synchronized");
thread->set_vm_result(exception);
#ifdef CC_INTERP
return (address) -1;
#else
return Interpreter::remove_activation_entry();
#endif
}
{- -------------------------------------------
(1) (以下の do ループ内で, 対応する例外ハンドラを探す.
なおループになっているのは, 例外ハンドラを探す間で再帰的に例外が発生した場合に,
新しい例外の方を優先するため (後述))
---------------------------------------- -}
do {
should_repeat = false;
{- -------------------------------------------
(1.1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
---------------------------------------- -}
// assertions
#ifdef ASSERT
assert(h_exception.not_null(), "NULL exceptions should be handled by athrow");
assert(h_exception->is_oop(), "just checking");
// Check that exception is a subclass of Throwable, otherwise we have a VerifyError
if (!(h_exception->is_a(SystemDictionary::Throwable_klass()))) {
if (ExitVMOnVerifyError) vm_exit(-1);
ShouldNotReachHere();
}
#endif
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
// tracing
if (TraceExceptions) {
ttyLocker ttyl;
ResourceMark rm(thread);
tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", h_exception->print_value_string(), (address)h_exception());
tty->print_cr(" thrown in interpreter method <%s>", h_method->print_value_string());
tty->print_cr(" at bci %d for thread " INTPTR_FORMAT, current_bci, thread);
}
// Don't go paging in something which won't be used.
// else if (h_extable->length() == 0) {
// // disabled for now - interpreter is not using shortcut yet
// // (shortcut is not to call runtime if we have no exception handlers)
// // warning("performance bug: should not call runtime if method has no exception handlers");
// }
{- -------------------------------------------
(1.1) (デバッグ用の処理)
---------------------------------------- -}
// for AbortVMOnException flag
NOT_PRODUCT(Exceptions::debug_check_abort(h_exception));
{- -------------------------------------------
(1.1) methodOopDesc::fast_exception_handler_bci_for() を呼んで,
対応する例外ハンドラのエントリポイント(を表す BCI) を取得する
(もしこのメソッド内に対応するハンドラがなければ, -1 が返される).
なお, methodOopDesc::fast_exception_handler_bci_for() 内で
コンスタントプールからクラス情報を取得する際に例外が発生する可能性がある.
この場合, 新たに発生した例外の方を優先して, もう一度ループの先頭からやり直す.
(この挙動は JavaVM 仕様のどこに書いてある?? #TODO)
---------------------------------------- -}
// exception handler lookup
KlassHandle h_klass(THREAD, h_exception->klass());
handler_bci = h_method->fast_exception_handler_bci_for(h_klass, current_bci, THREAD);
if (HAS_PENDING_EXCEPTION) {
// We threw an exception while trying to find the exception handler.
// Transfer the new exception to the exception handle which will
// be set into thread local storage, and do another lookup for an
// exception handler for this exception, this time starting at the
// BCI of the exception handler which caused the exception to be
// thrown (bug 4307310).
h_exception = Handle(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
if (handler_bci >= 0) {
current_bci = handler_bci;
should_repeat = true;
}
}
} while (should_repeat == true);
{- -------------------------------------------
(1) (JVMTI のフック点)
---------------------------------------- -}
// notify JVMTI of an exception throw; JVMTI will detect if this is a first
// time throw or a stack unwinding throw and accordingly notify the debugger
if (JvmtiExport::can_post_on_exceptions()) {
JvmtiExport::post_exception_throw(thread, h_method(), bcp(thread), h_exception());
}
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
#ifdef CC_INTERP
address continuation = (address)(intptr_t) handler_bci;
#else
address continuation = NULL;
#endif
address handler_pc = NULL;
{- -------------------------------------------
(1) 以下の2つの条件に応じて, リターンする値を決める.
* このメソッド内に対応するハンドラがあったかどうか (= handler_bci が -1 かどうか)
* Stack Overflow 間近でスタックに空きがない状況かどうか (= JavaThread::reguard_stack() が false を返かどうか)
* ハンドラが見当たらなかった場合, またはスタックに空きがない場合
Interpreter::remove_activation_entry() を返すことにする.
(なお, このケースではついでに
対応する methodOop の interpreter_throwout_count() も増加させている.
これは JIT コンパイラが使用する模様.)
* それ以外の場合
例外ハンドラのエントリポイントの BCI に対応するテンプレートを返すことにする.
(ここで, Interpreter::dispatch_table から対応するテンプレートを取得しておく)
---------------------------------------- -}
if (handler_bci < 0 || !thread->reguard_stack((address) &continuation)) {
// Forward exception to callee (leaving bci/bcp untouched) because (a) no
// handler in this method, or (b) after a stack overflow there is not yet
// enough stack space available to reprotect the stack.
#ifndef CC_INTERP
continuation = Interpreter::remove_activation_entry();
#endif
// Count this for compilation purposes
h_method->interpreter_throwout_increment();
} else {
// handler in this method => change bci/bcp to handler bci/bcp and continue there
handler_pc = h_method->code_base() + handler_bci;
#ifndef CC_INTERP
set_bcp_and_mdp(handler_pc, thread);
continuation = Interpreter::dispatch_table(vtos)[*handler_pc];
#endif
}
{- -------------------------------------------
(1) (JVMTI のフック点)
---------------------------------------- -}
// notify debugger of an exception catch
// (this is good for exceptions caught in native methods as well)
if (JvmtiExport::can_post_on_exceptions()) {
JvmtiExport::notice_unwind_due_to_exception(thread, h_method(), handler_pc, h_exception(), (handler_pc != NULL));
}
{- -------------------------------------------
(1) 引数で指定されたスレッドの vm_result フィールドに例外オブジェクトを格納しておく.
---------------------------------------- -}
thread->set_vm_result(h_exception());
{- -------------------------------------------
(1) リターン
---------------------------------------- -}
return continuation;
IRT_END
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.