Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/wabt/interp/interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ struct LocalDesc {
struct CatchDesc {
Index tag_index;
u32 offset;
bool ref = false;
};

// Handlers for a catch-less `try` or `try-catch` block are included in the
Expand All @@ -314,6 +315,7 @@ struct HandlerDesc {
// Local stack heights at the handler site that need to be restored.
u32 values;
u32 exceptions;
bool catch_all_ref = false;
};

struct FuncDesc {
Expand Down
88 changes: 88 additions & 0 deletions src/interp/binary-reader-interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,10 @@ class BinaryReaderInterp : public BinaryReaderNop {
Result OnTableInitExpr(Index segment_index, Index table_index) override;
Result OnTernaryExpr(Opcode opcode) override;
Result OnThrowExpr(Index tag_index) override;
Result OnThrowRefExpr() override;
Result OnTryExpr(Type sig_type) override;
Result OnTryTableExpr(Type sig_type,
const CatchClauseVector& catches) override;
Result OnUnreachableExpr() override;
Result EndFunctionBody(Index index) override;
Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
Expand Down Expand Up @@ -1072,6 +1075,11 @@ Result BinaryReaderInterp::OnEndExpr() {
HandlerDesc& desc = func_->handlers[local_label->handler_desc_index];
desc.try_end_offset = istream_.end();
assert(desc.catches.size() == 0);
} else if (label_type == LabelType::TryTable) {
// TryTable blocks need a try_end_offset
Label* local_label = TopLabel();
HandlerDesc& desc = func_->handlers[local_label->handler_desc_index];
desc.try_end_offset = istream_.end();
} else if (label_type == LabelType::Catch) {
istream_.EmitCatchDrop(1);
}
Expand Down Expand Up @@ -1518,6 +1526,12 @@ Result BinaryReaderInterp::OnThrowExpr(Index tag_index) {
return Result::Ok;
}

Result BinaryReaderInterp::OnThrowRefExpr() {
CHECK_RESULT(validator_.OnThrowRef(GetLocation()));
istream_.Emit(Opcode::ThrowRef);
return Result::Ok;
}

Result BinaryReaderInterp::OnRethrowExpr(Index depth) {
Index catch_depth;
CHECK_RESULT(validator_.OnRethrow(GetLocation(), Var(depth, GetLocation())));
Expand Down Expand Up @@ -1549,6 +1563,80 @@ Result BinaryReaderInterp::OnTryExpr(Type sig_type) {
return Result::Ok;
}

Result BinaryReaderInterp::OnTryTableExpr(Type sig_type,
const CatchClauseVector& catches) {
// we can just emit the catch handlers beforehand, so long as we skip over
// them when entering the try.
CHECK_RESULT(validator_.BeginTryTable(GetLocation(), sig_type));

u32 exn_stack_height;
CHECK_RESULT(
validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height));
// NOTE: *NOT* GetLocalCount. we don't count the parameters, as they're not
// part of the frame.
u32 value_stack_height = validator_.type_stack_size() + local_count_;

HandlerDesc desc =
HandlerDesc{HandlerKind::Catch, Istream::kInvalidOffset,
Istream::kInvalidOffset, {},
{Istream::kInvalidOffset}, value_stack_height,
exn_stack_height};

istream_.Emit(Opcode::Br);
auto offset = istream_.EmitFixupU32();

bool has_catch_all = false;
for (const auto& raw_catch : catches) {
TableCatch catch_;
catch_.kind = raw_catch.kind;
catch_.tag = Var(raw_catch.tag, GetLocation());
catch_.target = Var(raw_catch.depth, GetLocation());
CHECK_RESULT(validator_.OnTryTableCatch(GetLocation(), catch_));
// stop emitting handlers after catch_all - but we must still validate the
// handlers we don't emit
if (has_catch_all) {
continue;
}
if (catch_.IsCatchAll()) {
has_catch_all = true;
desc.catch_all_ref = catch_.IsRef();
desc.catch_all_offset = istream_.end();
} else {
desc.catches.push_back(
CatchDesc{raw_catch.tag, istream_.end(), catch_.IsRef()});
}
// we can't use GetBrDropKeepCount because we're not in a real block.
SharedValidator::Label* vlabel;
CHECK_RESULT(validator_.GetLabel(raw_catch.depth, &vlabel));
// we keep the exception's results.
// (this has already been validated, above)
Index keep_count = vlabel->br_types().size();
// we drop everything between the current block and the br target.
// (we have already taken the TryTable block parameters into account, in
// BeginTryTable)
Index drop_count = validator_.type_stack_size() - vlabel->type_stack_limit;
Index catch_drop_count;
// we use the regular catch count
CHECK_RESULT(validator_.GetCatchCount(raw_catch.depth, &catch_drop_count));
// but increment, as we are semantically in a catch
catch_drop_count++;
EmitBr(raw_catch.depth, drop_count, keep_count, catch_drop_count);
}

CHECK_RESULT(validator_.EndTryTable(GetLocation(), sig_type));

desc.try_start_offset = istream_.end();

// as usual, the label is pushed after the catch handlers
PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
func_->handlers.size());
func_->handlers.push_back(std::move(desc));

istream_.ResolveFixupU32(offset);

return Result::Ok;
}

Result BinaryReaderInterp::OnCatchExpr(Index tag_index) {
CHECK_RESULT(
validator_.OnCatch(GetLocation(), Var(tag_index, GetLocation()), false));
Expand Down
17 changes: 15 additions & 2 deletions src/interp/interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1959,6 +1959,14 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) {
exceptions_[exceptions_.size() - exn_index - 1]};
return DoThrow(exn);
}
case O::ThrowRef: {
Ref ref = Pop<Ref>();
assert(store_.HasValueType(ref, ValueType::ExnRef));
// FIXME better error message?
TRAP_IF(ref == Ref::Null, "expected exnref, got null");
Exception::Ptr exn = store_.UnsafeGet<Exception>(ref);
return DoThrow(exn);
}

// The following opcodes are either never generated or should never be
// executed.
Expand All @@ -1973,13 +1981,12 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) {

case O::CallRef:
case O::Try:
case O::TryTable:
case O::Catch:
case O::CatchAll:
case O::Delegate:
case O::InterpData:
case O::Invalid:
case O::TryTable:
case O::ThrowRef:
WABT_UNREACHABLE;
break;
}
Expand Down Expand Up @@ -2616,6 +2623,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
Tag::Ptr exn_tag{store_, exn->tag()};
bool popped_frame = false;
bool had_catch_all = false;
bool target_exnref = false;

// DoThrow is responsible for unwinding the stack at the point at which an
// exception is thrown, and also branching to the appropriate catch within
Expand Down Expand Up @@ -2653,6 +2661,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
target_offset = _catch.offset;
target_values = (*iter).values;
target_exceptions = (*iter).exceptions;
target_exnref = _catch.ref;
goto found_handler;
}
}
Expand All @@ -2661,6 +2670,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
target_values = (*iter).values;
target_exceptions = (*iter).exceptions;
had_catch_all = true;
target_exnref = handler.catch_all_ref;
goto found_handler;
}
}
Expand Down Expand Up @@ -2698,6 +2708,9 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
if (!had_catch_all) {
PushValues(exn_tag->type().signature, exn->args());
}
if (target_exnref) {
Push(exn.ref());
}
return RunResult::Ok;
}

Expand Down
4 changes: 2 additions & 2 deletions src/interp/istream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Instr Istream::Read(Offset* offset) const {
case Opcode::Nop:
case Opcode::Return:
case Opcode::Unreachable:
case Opcode::ThrowRef:
case Opcode::RefNull:
// 0 immediates, 0 operands.
instr.kind = InstrKind::Imm_0_Op_0;
Expand Down Expand Up @@ -793,9 +794,8 @@ Instr Istream::Read(Offset* offset) const {
case Opcode::Invalid:
case Opcode::Loop:
case Opcode::Try:
case Opcode::ReturnCall:
case Opcode::TryTable:
case Opcode::ThrowRef:
case Opcode::ReturnCall:
// Not used.
break;
}
Expand Down
19 changes: 19 additions & 0 deletions test/spec/exception-handling/throw_ref.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
;;; TOOL: run-interp-spec
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/throw_ref.wast
;;; ARGS*: --enable-exceptions
(;; STDOUT ;;;
out/test/spec/exception-handling/throw_ref.wast:99: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:101: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:104: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:106: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:108: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:109: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:115: assert_exception passed
out/test/spec/exception-handling/throw_ref.wast:117: assert_invalid passed:
out/test/spec/exception-handling/throw_ref/throw_ref.1.wasm:0000018: error: type mismatch in throw_ref, expected [exnref] but got []
0000018: error: OnThrowRefExpr callback failed
out/test/spec/exception-handling/throw_ref.wast:118: assert_invalid passed:
out/test/spec/exception-handling/throw_ref/throw_ref.2.wasm:000001a: error: type mismatch in throw_ref, expected [exnref] but got []
000001a: error: OnThrowRefExpr callback failed
15/15 tests passed.
;;; STDOUT ;;)
41 changes: 41 additions & 0 deletions test/spec/exception-handling/try_table.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
;;; TOOL: run-interp-spec
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/try_table.wast
;;; ARGS*: --enable-exceptions --enable-tail-call
(;; STDOUT ;;;
out/test/spec/exception-handling/try_table.wast:261: assert_trap passed: unreachable executed
out/test/spec/exception-handling/try_table.wast:264: assert_trap passed: integer divide by zero
out/test/spec/exception-handling/try_table.wast:268: assert_exception passed
out/test/spec/exception-handling/try_table.wast:272: assert_exception passed
out/test/spec/exception-handling/try_table.wast:310: assert_exception passed
out/test/spec/exception-handling/try_table.wast:311: assert_exception passed
out/test/spec/exception-handling/try_table.wast:340: assert_malformed passed:
out/test/spec/exception-handling/try_table/try_table.3.wat:1:16: error: unexpected token "catch_all", expected an instr.
(module (func (catch_all)))
^^^^^^^^^
out/test/spec/exception-handling/try_table.wast:345: assert_malformed passed:
out/test/spec/exception-handling/try_table/try_table.4.wat:1:25: error: unexpected token "catch", expected an instr.
(module (tag $e) (func (catch $e)))
^^^^^
out/test/spec/exception-handling/try_table.wast:360: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.6.wasm:000001c: error: type mismatch in try table, expected [i32] but got []
000001c: error: OnEndExpr callback failed
out/test/spec/exception-handling/try_table.wast:364: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.7.wasm:000001e: error: type mismatch in try table, expected [i32] but got [i64]
000001e: error: OnEndExpr callback failed
out/test/spec/exception-handling/try_table.wast:369: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.8.wasm:0000022: error: catch signature doesn't match target: expected [exnref], got []
0000022: error: OnTryTableExpr callback failed
out/test/spec/exception-handling/try_table.wast:373: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.9.wasm:0000026: error: catch signature doesn't match target: expected [], got [exnref]
0000026: error: OnTryTableExpr callback failed
out/test/spec/exception-handling/try_table.wast:377: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.10.wasm:000001c: error: catch signature doesn't match target: expected [exnref], got []
000001c: error: OnTryTableExpr callback failed
out/test/spec/exception-handling/try_table.wast:381: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.11.wasm:000001d: error: catch signature doesn't match target: expected [], got [exnref]
000001d: error: OnTryTableExpr callback failed
out/test/spec/exception-handling/try_table.wast:385: assert_invalid passed:
out/test/spec/exception-handling/try_table/try_table.12.wasm:0000028: error: catch signature doesn't match target: expected [i64, exnref], got [i32, exnref]
0000028: error: OnTryTableExpr callback failed
53/53 tests passed.
;;; STDOUT ;;)
Loading