[wasm] Add test for handling "unreachable" trap.

This adds a test case to check consistency of how an "unreachable" trap
is handled by a surrounding "try" block in case those two operations are
in different functions (i.e. not local to one function body). It also
fixes a DCHECK for an as-of-yet untested interpreter state transition.

R=clemensh@chromium.org
TEST=cctest/test-run-wasm-exceptions
BUG=v8:8729

Change-Id: I432c48d0bc664f7ab092aaafef6dfa29c5f262fd
Reviewed-on: https://chromium-review.googlesource.com/c/1454605
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59429}
This commit is contained in:
Michael Starzinger 2019-02-07 11:02:06 +01:00 committed by Commit Bot
parent b3726e9041
commit 0999709cf2
5 changed files with 69 additions and 20 deletions

View File

@ -215,9 +215,7 @@ class InterpreterHandle {
WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
Handle<Object> exception =
isolate_->factory()->NewWasmRuntimeError(message_id);
isolate_->Throw(*exception);
// Handle this exception locally within the activation.
auto result = thread->HandleException(isolate_);
auto result = thread->RaiseException(isolate_, exception);
if (result == WasmInterpreter::Thread::HANDLED) break;
// If no local handler was found, we fall-thru to {STOPPED}.
DCHECK_EQ(WasmInterpreter::State::STOPPED, thread->state());

View File

@ -1267,6 +1267,19 @@ class ThreadImpl {
return activations_[id].fp;
}
WasmInterpreter::Thread::ExceptionHandlingResult RaiseException(
Isolate* isolate, Handle<Object> exception) {
DCHECK_EQ(WasmInterpreter::TRAPPED, state_);
isolate->Throw(*exception); // Will check that none is pending.
if (HandleException(isolate) == WasmInterpreter::Thread::UNWOUND) {
DCHECK_EQ(WasmInterpreter::STOPPED, state_);
return WasmInterpreter::Thread::UNWOUND;
}
state_ = WasmInterpreter::PAUSED;
return WasmInterpreter::Thread::HANDLED;
}
private:
// Handle a thrown exception. Returns whether the exception was handled inside
// the current activation. Unwinds the interpreted stack accordingly.
WasmInterpreter::Thread::ExceptionHandlingResult HandleException(
@ -1301,7 +1314,6 @@ class ThreadImpl {
return WasmInterpreter::Thread::UNWOUND;
}
private:
// Entries on the stack of functions being evaluated.
struct Frame {
InterpreterCode* code;
@ -3283,8 +3295,9 @@ WasmInterpreter::State WasmInterpreter::Thread::Run(int num_steps) {
void WasmInterpreter::Thread::Pause() { return ToImpl(this)->Pause(); }
void WasmInterpreter::Thread::Reset() { return ToImpl(this)->Reset(); }
WasmInterpreter::Thread::ExceptionHandlingResult
WasmInterpreter::Thread::HandleException(Isolate* isolate) {
return ToImpl(this)->HandleException(isolate);
WasmInterpreter::Thread::RaiseException(Isolate* isolate,
Handle<Object> exception) {
return ToImpl(this)->RaiseException(isolate, exception);
}
pc_t WasmInterpreter::Thread::GetBreakpointPc() {
return ToImpl(this)->GetBreakpointPc();

View File

@ -85,13 +85,14 @@ struct InterpretedFrameDeleter {
class V8_EXPORT_PRIVATE WasmInterpreter {
public:
// State machine for a Thread:
// +---------Run()/Step()--------+
// V |
// STOPPED ---Run()--> RUNNING ------Pause()-----+-> PAUSED
// ^ | | | | /
// +- HandleException -+ | | +--- Breakpoint ---+
// | |
// | +---------- Trap --------------> TRAPPED
// +----------------------------------------------------------+
// | +--------Run()/Step()---------+ |
// V V | |
// STOPPED ---Run()--> RUNNING ------Pause()-----+-> PAUSED <--+
// ^ | | | | / |
// +--- Exception ---+ | | +--- Breakpoint ---+ RaiseException() <--+
// | | |
// | +---------- Trap --------------> TRAPPED --------+
// +----------- Finish -------------> FINISHED
enum State { STOPPED, RUNNING, PAUSED, FINISHED, TRAPPED };
@ -121,9 +122,12 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
State Step() { return Run(1); }
void Pause();
void Reset();
// Handle the pending exception in the passed isolate. Unwind the stack
// accordingly. Return whether the exception was handled inside wasm.
ExceptionHandlingResult HandleException(Isolate* isolate);
// Raise an exception in the current activation and unwind the stack
// accordingly. Return whether the exception was handled inside wasm:
// - HANDLED: Activation at handler position and in {PAUSED} state.
// - UNWOUND: Frames unwound, exception pending, and in {STOPPED} state.
ExceptionHandlingResult RaiseException(Isolate*, Handle<Object> exception);
// Stack inspection and modification.
pc_t GetBreakpointPc();

View File

@ -158,6 +158,37 @@ WASM_EXEC_TEST(TryCatchTrapTypeError) {
r.CheckCallViaJS(kResult1, 1);
}
// TODO(8729): The semantics of this are not yet specified and might change,
// this test aims at keeping semantics of various execution tiers consistent.
// TODO(mstarzinger): Add further tests for different kinds of traps.
WASM_EXEC_TEST(TryCatchTrapUnreachable) {
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(eh);
WasmRunner<uint32_t, uint32_t> r(execution_tier, nullptr, "main",
kRuntimeExceptionSupport);
constexpr uint32_t kResult0 = 23;
constexpr uint32_t kResult1 = 42;
// Build a trapping helper function.
WasmFunctionCompiler& trap_func = r.NewFunction(sigs.i_ii());
BUILD(trap_func, WASM_UNREACHABLE);
// Build the main test function.
BUILD(r, WASM_TRY_CATCH_T(
kWasmI32,
WASM_STMTS(WASM_I32V(kResult1),
WASM_IF(WASM_I32_EQZ(WASM_GET_LOCAL(0)),
WASM_STMTS(WASM_CALL_FUNCTION(
trap_func.function_index(),
WASM_I32V(7), WASM_I32V(9)),
WASM_DROP))),
WASM_STMTS(WASM_DROP, WASM_I32V(kResult0))));
// Need to call through JS to allow for creation of stack traces.
r.CheckCallViaJS(kResult0, 0);
r.CheckCallViaJS(kResult1, 1);
}
} // namespace test_run_wasm_exceptions
} // namespace wasm
} // namespace internal

View File

@ -438,7 +438,7 @@ TEST(TestPossibleNondeterminism) {
TEST(WasmInterpreterActivations) {
WasmRunner<void> r(ExecutionTier::kInterpreter);
Isolate* isolate = r.main_isolate();
BUILD(r, WASM_NOP);
BUILD(r, WASM_UNREACHABLE);
WasmInterpreter* interpreter = r.interpreter();
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
@ -451,17 +451,20 @@ TEST(WasmInterpreterActivations) {
thread->InitFrame(r.function(), nullptr);
CHECK_EQ(2, thread->NumActivations());
CHECK_EQ(2, thread->GetFrameCount());
isolate->set_pending_exception(Smi::kZero);
thread->HandleException(isolate);
CHECK_EQ(WasmInterpreter::TRAPPED, thread->Run());
thread->RaiseException(isolate, handle(Smi::kZero, isolate));
CHECK_EQ(1, thread->GetFrameCount());
CHECK_EQ(2, thread->NumActivations());
thread->FinishActivation(act1);
isolate->clear_pending_exception();
CHECK_EQ(1, thread->GetFrameCount());
CHECK_EQ(1, thread->NumActivations());
thread->HandleException(isolate);
CHECK_EQ(WasmInterpreter::TRAPPED, thread->Run());
thread->RaiseException(isolate, handle(Smi::kZero, isolate));
CHECK_EQ(0, thread->GetFrameCount());
CHECK_EQ(1, thread->NumActivations());
thread->FinishActivation(act0);
isolate->clear_pending_exception();
CHECK_EQ(0, thread->NumActivations());
}