[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: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43859}
This commit is contained in:
Clemens Hammacher 2017-03-16 12:54:31 +01:00 committed by Commit Bot
parent b3507ff022
commit 783f68c53b
7 changed files with 63 additions and 12 deletions

View File

@ -201,9 +201,13 @@ RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
isolate->set_context(instance->compiled_module()->ptr_to_native_context());
trap_handler::ClearThreadInWasm();
instance->debug_info()->RunInterpreter(func_index, arg_buffer);
bool success = instance->debug_info()->RunInterpreter(func_index, arg_buffer);
trap_handler::SetThreadInWasm();
if (!success) {
DCHECK(isolate->has_pending_exception());
return isolate->heap()->exception();
}
return isolate->heap()->undefined_value();
}

View File

@ -125,7 +125,7 @@ class InterpreterHandle {
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);
FunctionSig* sig = module()->functions[func_index].sig;
DCHECK_GE(kMaxInt, sig->parameter_count());
@ -155,7 +155,8 @@ class InterpreterHandle {
// We do not support reentering an already running interpreter at the moment
// (like INTERPRETER -> JS -> WASM -> INTERPRETER).
DCHECK(thread->state() == WasmInterpreter::STOPPED ||
thread->state() == WasmInterpreter::FINISHED);
thread->state() == WasmInterpreter::FINISHED ||
thread->state() == WasmInterpreter::TRAPPED);
thread->Reset();
thread->InitFrame(&module()->functions[func_index], wasm_args.start());
bool finished = false;
@ -170,10 +171,15 @@ class InterpreterHandle {
// Perfect, just break the switch and exit the loop.
finished = true;
break;
case WasmInterpreter::State::TRAPPED:
// TODO(clemensh): Generate appropriate JS exception.
UNIMPLEMENTED();
break;
case WasmInterpreter::State::TRAPPED: {
int message_id =
WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
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.
case WasmInterpreter::State::STOPPED:
case WasmInterpreter::State::RUNNING:
@ -203,6 +209,7 @@ class InterpreterHandle {
UNREACHABLE();
}
}
return true;
}
WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
@ -474,9 +481,9 @@ void WasmDebugInfo::PrepareStep(StepAction 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);
GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
return GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
arg_buffer);
}

View File

@ -1059,6 +1059,8 @@ class ThreadImpl {
return stack_[index];
}
TrapReason GetTrapReason() { return trap_reason_; }
pc_t GetBreakpointPc() { return break_pc_; }
bool PossibleNondeterminism() { return possible_nondeterminism_; }
@ -1913,6 +1915,9 @@ InterpretedFrame WasmInterpreter::Thread::GetMutableFrame(int index) {
WasmVal WasmInterpreter::Thread::GetReturnValue(int index) {
return ToImpl(this)->GetReturnValue(index);
}
TrapReason WasmInterpreter::Thread::GetTrapReason() {
return ToImpl(this)->GetTrapReason();
}
bool WasmInterpreter::Thread::PossibleNondeterminism() {
return ToImpl(this)->PossibleNondeterminism();
}

View File

@ -148,6 +148,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
const InterpretedFrame GetFrame(int index);
InterpretedFrame GetMutableFrame(int index);
WasmVal GetReturnValue(int index = 0);
TrapReason GetTrapReason();
// Returns true if the thread executed an instruction which may produce
// nondeterministic results, e.g. float div, float sqrt, and float mul,

View File

@ -453,7 +453,11 @@ class WasmDebugInfo : public FixedArray {
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
// offset>. The list is ordered bottom-to-top, i.e. caller before callee.

View File

@ -78,3 +78,33 @@ function checkStack(stack, expected_lines) {
assertEquals(expected, ret);
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$/
]);
}
})();

View File

@ -98,7 +98,7 @@ class WasmFunctionBuilder {
addBody(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;
// Automatically add the end for the function block to the body.