Commit 783f68c5 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] [interpreter] Throw exception on trap

This behaviour was missing before. If a trap is encountered in the
interpreter, we now throw the right error. With test.

R=titzer@chromium.org, ahaas@chromium.org
BUG=v8:5822

Change-Id: I09c23d15fcde32ec586fb6d3094a5ec49155a9a2
Reviewed-on: https://chromium-review.googlesource.com/453839
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43859}
parent b3507ff0
...@@ -201,9 +201,13 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) { ...@@ -201,9 +201,13 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
isolate->set_context(instance->compiled_module()->ptr_to_native_context()); isolate->set_context(instance->compiled_module()->ptr_to_native_context());
trap_handler::ClearThreadInWasm(); trap_handler::ClearThreadInWasm();
instance->debug_info()->RunInterpreter(func_index, arg_buffer); bool success = instance->debug_info()->RunInterpreter(func_index, arg_buffer);
trap_handler::SetThreadInWasm(); trap_handler::SetThreadInWasm();
if (!success) {
DCHECK(isolate->has_pending_exception());
return isolate->heap()->exception();
}
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -125,7 +125,7 @@ class InterpreterHandle { ...@@ -125,7 +125,7 @@ class InterpreterHandle {
return interpreter()->GetThread(0)->GetFrameCount(); return interpreter()->GetThread(0)->GetFrameCount();
} }
void Execute(uint32_t func_index, uint8_t* arg_buffer) { bool Execute(uint32_t func_index, uint8_t* arg_buffer) {
DCHECK_GE(module()->functions.size(), func_index); DCHECK_GE(module()->functions.size(), func_index);
FunctionSig* sig = module()->functions[func_index].sig; FunctionSig* sig = module()->functions[func_index].sig;
DCHECK_GE(kMaxInt, sig->parameter_count()); DCHECK_GE(kMaxInt, sig->parameter_count());
...@@ -155,7 +155,8 @@ class InterpreterHandle { ...@@ -155,7 +155,8 @@ class InterpreterHandle {
// We do not support reentering an already running interpreter at the moment // We do not support reentering an already running interpreter at the moment
// (like INTERPRETER -> JS -> WASM -> INTERPRETER). // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
DCHECK(thread->state() == WasmInterpreter::STOPPED || DCHECK(thread->state() == WasmInterpreter::STOPPED ||
thread->state() == WasmInterpreter::FINISHED); thread->state() == WasmInterpreter::FINISHED ||
thread->state() == WasmInterpreter::TRAPPED);
thread->Reset(); thread->Reset();
thread->InitFrame(&module()->functions[func_index], wasm_args.start()); thread->InitFrame(&module()->functions[func_index], wasm_args.start());
bool finished = false; bool finished = false;
...@@ -170,10 +171,15 @@ class InterpreterHandle { ...@@ -170,10 +171,15 @@ class InterpreterHandle {
// Perfect, just break the switch and exit the loop. // Perfect, just break the switch and exit the loop.
finished = true; finished = true;
break; break;
case WasmInterpreter::State::TRAPPED: case WasmInterpreter::State::TRAPPED: {
// TODO(clemensh): Generate appropriate JS exception. int message_id =
UNIMPLEMENTED(); WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
break; Handle<Object> exception = isolate_->factory()->NewWasmRuntimeError(
static_cast<MessageTemplate::Template>(message_id));
isolate_->Throw(*exception);
// And hard exit the execution.
return false;
} break;
// STOPPED and RUNNING should never occur here. // STOPPED and RUNNING should never occur here.
case WasmInterpreter::State::STOPPED: case WasmInterpreter::State::STOPPED:
case WasmInterpreter::State::RUNNING: case WasmInterpreter::State::RUNNING:
...@@ -203,6 +209,7 @@ class InterpreterHandle { ...@@ -203,6 +209,7 @@ class InterpreterHandle {
UNREACHABLE(); UNREACHABLE();
} }
} }
return true;
} }
WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) { WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
...@@ -474,10 +481,10 @@ void WasmDebugInfo::PrepareStep(StepAction step_action) { ...@@ -474,10 +481,10 @@ void WasmDebugInfo::PrepareStep(StepAction step_action) {
GetInterpreterHandle(this)->PrepareStep(step_action); GetInterpreterHandle(this)->PrepareStep(step_action);
} }
void WasmDebugInfo::RunInterpreter(int func_index, uint8_t* arg_buffer) { bool WasmDebugInfo::RunInterpreter(int func_index, uint8_t* arg_buffer) {
DCHECK_LE(0, func_index); DCHECK_LE(0, func_index);
GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index), return GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
arg_buffer); arg_buffer);
} }
std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack( std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
......
...@@ -1059,6 +1059,8 @@ class ThreadImpl { ...@@ -1059,6 +1059,8 @@ class ThreadImpl {
return stack_[index]; return stack_[index];
} }
TrapReason GetTrapReason() { return trap_reason_; }
pc_t GetBreakpointPc() { return break_pc_; } pc_t GetBreakpointPc() { return break_pc_; }
bool PossibleNondeterminism() { return possible_nondeterminism_; } bool PossibleNondeterminism() { return possible_nondeterminism_; }
...@@ -1913,6 +1915,9 @@ InterpretedFrame WasmInterpreter::Thread::GetMutableFrame(int index) { ...@@ -1913,6 +1915,9 @@ InterpretedFrame WasmInterpreter::Thread::GetMutableFrame(int index) {
WasmVal WasmInterpreter::Thread::GetReturnValue(int index) { WasmVal WasmInterpreter::Thread::GetReturnValue(int index) {
return ToImpl(this)->GetReturnValue(index); return ToImpl(this)->GetReturnValue(index);
} }
TrapReason WasmInterpreter::Thread::GetTrapReason() {
return ToImpl(this)->GetTrapReason();
}
bool WasmInterpreter::Thread::PossibleNondeterminism() { bool WasmInterpreter::Thread::PossibleNondeterminism() {
return ToImpl(this)->PossibleNondeterminism(); return ToImpl(this)->PossibleNondeterminism();
} }
......
...@@ -148,6 +148,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter { ...@@ -148,6 +148,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
const InterpretedFrame GetFrame(int index); const InterpretedFrame GetFrame(int index);
InterpretedFrame GetMutableFrame(int index); InterpretedFrame GetMutableFrame(int index);
WasmVal GetReturnValue(int index = 0); WasmVal GetReturnValue(int index = 0);
TrapReason GetTrapReason();
// Returns true if the thread executed an instruction which may produce // Returns true if the thread executed an instruction which may produce
// nondeterministic results, e.g. float div, float sqrt, and float mul, // nondeterministic results, e.g. float div, float sqrt, and float mul,
......
...@@ -453,7 +453,11 @@ class WasmDebugInfo : public FixedArray { ...@@ -453,7 +453,11 @@ class WasmDebugInfo : public FixedArray {
void PrepareStep(StepAction); void PrepareStep(StepAction);
void RunInterpreter(int func_index, uint8_t* arg_buffer); // Execute the specified funtion in the interpreter. Read arguments from
// arg_buffer.
// Returns true if exited regularly, false if a trap occured. In the latter
// case, a pending exception will have been set on the isolate.
bool RunInterpreter(int func_index, uint8_t* arg_buffer);
// Get the stack of the wasm interpreter as pairs of <function index, byte // Get the stack of the wasm interpreter as pairs of <function index, byte
// offset>. The list is ordered bottom-to-top, i.e. caller before callee. // offset>. The list is ordered bottom-to-top, i.e. caller before callee.
......
...@@ -78,3 +78,33 @@ function checkStack(stack, expected_lines) { ...@@ -78,3 +78,33 @@ function checkStack(stack, expected_lines) {
assertEquals(expected, ret); assertEquals(expected, ret);
assertArrayEquals([args[0], args[0] + 1, args[1]], passed_test_args); assertArrayEquals([args[0], args[0] + 1, args[1]], passed_test_args);
})(); })();
(function testTrap() {
var builder = new WasmModuleBuilder();
var foo_idx = builder.addFunction('foo', kSig_v_v)
.addBody([kExprNop, kExprNop, kExprUnreachable])
.index;
builder.addFunction('main', kSig_v_v)
.addBody([kExprNop, kExprCallFunction, foo_idx])
.exportFunc();
var instance = builder.instantiate();
// Test that this does not mess up internal state by executing it three times.
for (var i = 0; i < 3; ++i) {
var interpreted_before = % WasmNumInterpretedCalls(instance);
var stack;
try {
instance.exports.main();
assertUnreachable();
} catch (e) {
stack = e.stack;
}
assertEquals(interpreted_before + 2, % WasmNumInterpretedCalls(instance));
checkStack(stripPath(stack), [
'RuntimeError: unreachable', // -
' at foo (<WASM>[0]+3)', // -
' at main (<WASM>[1]+2)', // -
/^ at testTrap \(interpreter.js:\d+:24\)$/, // -
/^ at interpreter.js:\d+:3$/
]);
}
})();
...@@ -98,7 +98,7 @@ class WasmFunctionBuilder { ...@@ -98,7 +98,7 @@ class WasmFunctionBuilder {
addBody(body) { addBody(body) {
for (let b of body) { for (let b of body) {
if (typeof b != 'number') throw new Error("invalid body"); if (typeof b != 'number') throw new Error('invalid body: ' + body);
} }
this.body = body; this.body = body;
// Automatically add the end for the function block to the body. // Automatically add the end for the function block to the body.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment