[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:
parent
b3507ff022
commit
783f68c53b
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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,10 +481,10 @@ 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),
|
||||
arg_buffer);
|
||||
return GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
|
||||
arg_buffer);
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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$/
|
||||
]);
|
||||
}
|
||||
})();
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user