diff options
-rwxr-xr-x | check.py | 3 | ||||
-rw-r--r-- | src/shell-interface.h | 2 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 2 | ||||
-rw-r--r-- | src/tools/wasm-shell.cpp | 2 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 88 | ||||
-rw-r--r-- | src/wasm/wasm-interpreter.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 6 | ||||
-rw-r--r-- | test/spec/exception-handling.wast | 223 |
8 files changed, 181 insertions, 149 deletions
@@ -189,9 +189,6 @@ def run_spec_tests(): # windows has some failures that need to be investigated if base == 'names.wast' and shared.skip_if_on_windows('spec: ' + base): continue - # FIXME Reenable this after updating interpreter for EH - if base == 'exception-handling.wast': - continue def run_spec_test(wast): cmd = shared.WASM_SHELL + [wast] diff --git a/src/shell-interface.h b/src/shell-interface.h index d82885ced..c429c3a29 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -232,7 +232,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { throw TrapException(); } - void throwException(Literal exn) override { throw WasmException(exn); } + void throwException(const WasmException& exn) override { throw exn; } }; } // namespace wasm diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 9e78cfb7a..4ad9979cd 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -292,7 +292,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { throw FailToEvalException(std::string("trap: ") + why); } - void throwException(Literal exn) override { + void throwException(const WasmException& exn) override { std::stringstream ss; ss << "exception thrown: " << exn; throw FailToEvalException(ss.str()); diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp index 08c76ac9c..20d4b9344 100644 --- a/src/tools/wasm-shell.cpp +++ b/src/tools/wasm-shell.cpp @@ -208,7 +208,7 @@ static void run_asserts(Name moduleName, } catch (const TrapException&) { trapped = true; } catch (const WasmException& e) { - std::cout << "[exception thrown: " << e.exn << "]" << std::endl; + std::cout << "[exception thrown: " << e << "]" << std::endl; trapped = true; } if (id == ASSERT_RETURN) { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 9fc9734eb..ac9fd80c8 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -41,9 +41,10 @@ namespace wasm { struct WasmException { - WasmException(Literal exn) : exn(exn) {} - Literal exn; + Name event; + Literals values; }; +std::ostream& operator<<(std::ostream& o, const WasmException& exn); using namespace cashew; @@ -1344,31 +1345,15 @@ public: return flow; } NOTE_EVAL1(curr->event); - auto exn = std::make_unique<ExceptionPackage>(); - exn->event = curr->event; + WasmException exn; + exn.event = curr->event; for (auto item : arguments) { - exn->values.push_back(item); + exn.values.push_back(item); } - throwException(Literal::makeExn(std::move(exn))); + throwException(exn); WASM_UNREACHABLE("throw"); } - Flow visitRethrow(Rethrow* curr) { - // FIXME Update the implementation to match the new spec - NOTE_ENTER("Rethrow"); - WASM_UNREACHABLE("unimp"); - /* - Flow flow = visit(curr->exnref); - if (flow.breaking()) { - return flow; - } - const auto& value = flow.getSingleValue(); - if (value.isNull()) { - trap("rethrow: argument is null"); - } - throwException(value); - WASM_UNREACHABLE("rethrow"); - */ - } + Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("unimp"); } Flow visitBrOnExn(BrOnExn* curr) { NOTE_ENTER("BrOnExn"); Flow flow = this->visit(curr->exnref); @@ -1660,7 +1645,9 @@ public: virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); } - virtual void throwException(Literal exn) { WASM_UNREACHABLE("unimp"); } + virtual void throwException(const WasmException& exn) { + WASM_UNREACHABLE("unimp"); + } private: // Truncate the value if we need to. The storage is just a list of Literals, @@ -1943,10 +1930,14 @@ public: NOTE_ENTER("Try"); return Flow(NONCONSTANT_FLOW); } + Flow visitRethrow(Rethrow* curr) { + NOTE_ENTER("Rethrow"); + return Flow(NONCONSTANT_FLOW); + } void trap(const char* why) override { throw NonconstantException(); } - virtual void throwException(Literal exn) override { + virtual void throwException(const WasmException& exn) override { throw NonconstantException(); } }; @@ -1995,7 +1986,7 @@ public: SubType& instance) = 0; virtual bool growMemory(Address oldSize, Address newSize) = 0; virtual void trap(const char* why) = 0; - virtual void throwException(Literal exnref) = 0; + virtual void throwException(const WasmException& exn) = 0; // the default impls for load and store switch on the sizes. you can either // customize load/store, or the sub-functions which they call @@ -2152,7 +2143,7 @@ public: GlobalManager globals; // Multivalue ABI support (see push/pop). - std::vector<Literal> multiValues; + std::vector<Literals> multiValues; ModuleInstanceBase(Module& wasm, ExternalInterface* externalInterface) : wasm(wasm), externalInterface(externalInterface) { @@ -2315,6 +2306,7 @@ private: : public ExpressionRunner<RuntimeExpressionRunner> { ModuleInstanceBase& instance; FunctionScope& scope; + SmallVector<WasmException, 4> exceptionStack; public: RuntimeExpressionRunner(ModuleInstanceBase& instance, @@ -2953,22 +2945,50 @@ private: return {}; } Flow visitTry(Try* curr) { - // FIXME Update the implementation to match the new spec - WASM_UNREACHABLE("unimp"); - /* NOTE_ENTER("Try"); try { return this->visit(curr->body); } catch (const WasmException& e) { - instance.multiValues.push_back(e.exn); - return this->visit(curr->catchBody); + auto processCatchBody = [&](Expression* catchBody) { + // Push the current exception onto the exceptionStack in case + // 'rethrow's use it + exceptionStack.push_back(e); + // We need to pop exceptionStack in either case: when the catch body + // exits normally or when a new exception is thrown + Flow ret; + try { + ret = this->visit(catchBody); + } catch (const WasmException& e) { + exceptionStack.pop_back(); + throw; + } + exceptionStack.pop_back(); + return ret; + }; + + for (size_t i = 0; i < curr->catchEvents.size(); i++) { + if (curr->catchEvents[i] == e.event) { + instance.multiValues.push_back(e.values); + return processCatchBody(curr->catchBodies[i]); + } + } + if (curr->hasCatchAll()) { + return processCatchBody(curr->catchBodies.back()); + } + // This exception is not caught by this try-catch. Rethrow it. + throw; } - */ + } + Flow visitRethrow(Rethrow* curr) { + assert(exceptionStack.size() > curr->depth); + throwException(exceptionStack[exceptionStack.size() - 1 - curr->depth]); + WASM_UNREACHABLE("rethrow"); } Flow visitPop(Pop* curr) { NOTE_ENTER("Pop"); assert(!instance.multiValues.empty()); auto ret = instance.multiValues.back(); + assert(curr->type == ret.getType()); instance.multiValues.pop_back(); return ret; } @@ -2977,7 +2997,7 @@ private: instance.externalInterface->trap(why); } - void throwException(Literal exn) override { + void throwException(const WasmException& exn) override { instance.externalInterface->throwException(exn); } diff --git a/src/wasm/wasm-interpreter.cpp b/src/wasm/wasm-interpreter.cpp index ad4b565fe..f37758808 100644 --- a/src/wasm/wasm-interpreter.cpp +++ b/src/wasm/wasm-interpreter.cpp @@ -19,4 +19,8 @@ void Indenter::print() { } #endif // WASM_INTERPRETER_DEBUG +std::ostream& operator<<(std::ostream& o, const WasmException& exn) { + return o << exn.event << " " << exn.values; +} + } // namespace wasm diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index e7055a8b0..1557240bb 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2097,10 +2097,8 @@ void FunctionValidator::visitRethrow(Rethrow* curr) { Type(Type::unreachable), curr, "rethrow's type must be unreachable"); - // TODO Allow non-zero depths and Validate the depth field. The current LLVM - // toolchain only generates depth 0 for C++ support. - shouldBeEqual( - curr->depth, (Index)0, curr, "rethrow only support depth 0 at the moment"); + // TODO Validate the depth field. The current LLVM toolchain only generates + // depth 0 for C++ support. } void FunctionValidator::visitBrOnExn(BrOnExn* curr) { diff --git a/test/spec/exception-handling.wast b/test/spec/exception-handling.wast index 05ecb97a9..d951441e0 100644 --- a/test/spec/exception-handling.wast +++ b/test/spec/exception-handling.wast @@ -1,6 +1,7 @@ (module (event $e-v (attr 0)) (event $e-i32 (attr 0) (param i32)) + (event $e-f32 (attr 0) (param f32)) (event $e-i32-f32 (attr 0) (param i32 f32)) (func $throw_single_value (export "throw_single_value") @@ -11,17 +12,13 @@ (throw $e-i32-f32 (i32.const 3) (f32.const 3.5)) ) - (func (export "rethrow_null") - (rethrow (ref.null exn)) - ) - (func (export "try_nothrow") (result i32) (try (result i32) (do (i32.const 3) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (i32.const 0) ) ) @@ -32,178 +29,201 @@ (do (throw $e-i32 (i32.const 5)) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (i32.const 3) ) ) ) - (func (export "try_call_catch") (result i32) + (func (export "try_throw_nocatch") (result i32) (try (result i32) (do - (call $throw_single_value) - (unreachable) + (throw $e-i32 (i32.const 5)) ) - (catch - (drop (pop exnref)) + (catch $e-f32 + (drop (pop f32)) (i32.const 3) ) ) ) - (func (export "try_throw_rethrow") - (try + (func (export "try_throw_catchall") (result i32) + (try (result i32) (do (throw $e-i32 (i32.const 5)) ) - (catch - (rethrow (pop exnref)) + (catch $e-f32 + (drop (pop f32)) + (i32.const 4) + ) + (catch_all + (i32.const 3) ) ) ) - (func $try_call_rethrow (export "try_call_rethrow") - (try + (func (export "try_call_catch") (result i32) + (try (result i32) (do (call $throw_single_value) + (unreachable) ) - (catch - (rethrow (pop exnref)) + (catch $e-i32 + (pop i32) ) ) ) - (func (export "br_on_exn_null") (result i32) - (block $l0 (result i32) - (drop - (br_on_exn $l0 $e-i32 (ref.null exn)) - ) - (i32.const 0) - ) - ) - - (func (export "br_on_exn_match_no_value") (local $exn exnref) - (try + (func (export "try_throw_multivalue_catch") (result i32) (local $x (i32 f32)) + (try (result i32) (do - (throw $e-v) + (throw $e-i32-f32 (i32.const 5) (f32.const 1.5)) ) - (catch - (local.set $exn (pop exnref)) - (block $l0 - (rethrow - (br_on_exn $l0 $e-v (local.get $exn)) - ) + (catch $e-i32-f32 + (local.set $x + (pop i32 f32) + ) + (tuple.extract 0 + (local.get $x) ) ) ) ) - (func (export "br_on_exn_match_single_value") (result i32) (local $exn exnref) - (try (result i32) + (func (export "try_throw_rethrow") + (try (do (throw $e-i32 (i32.const 5)) ) - (catch - (local.set $exn (pop exnref)) - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e-i32 (local.get $exn)) - ) - ) + (catch $e-i32 + (drop (pop i32)) + (rethrow 0) ) ) ) - (func (export "br_on_exn_match_multiple_values") (result i32 f32) - (local $exn exnref) - (try (result i32 f32) + (func (export "try_call_rethrow") + (try (do - (throw $e-i32-f32 (i32.const 3) (f32.const 3.5)) + (call $throw_single_value) ) - (catch - (local.set $exn (pop exnref)) - (block $l0 (result i32 f32) - (rethrow - (br_on_exn $l0 $e-i32-f32 (local.get $exn)) - ) - ) + (catch_all + (rethrow 0) ) ) ) - (func (export "br_on_exn_dont_match") (local $exn exnref) - (try + (func (export "rethrow_depth_test1") (result i32) + (try (result i32) (do - (throw $e-i32 (i32.const 5)) - ) - (catch - (local.set $exn (pop exnref)) - (block $l0 - (rethrow - (br_on_exn $l0 $e-v (local.get $exn)) + (try + (do + (throw $e-i32 (i32.const 1)) + ) + (catch_all + (try + (do + (throw $e-i32 (i32.const 2)) + ) + (catch $e-i32 + (drop (pop i32)) + (rethrow 0) ;; rethrow (i32.const 2) + ) + ) ) ) ) + (catch $e-i32 + (pop i32) ;; result is (i32.const 2) + ) ) ) - (func (export "call_br_on_exn") (result i32) (local $exn exnref) + ;; Can we handle rethrows with the depth > 0? + (func (export "rethrow_depth_test2") (result i32) (try (result i32) (do - (call $throw_single_value) - (unreachable) - ) - (catch - (local.set $exn (pop exnref)) - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e-i32 (local.get $exn)) + (try + (do + (throw $e-i32 (i32.const 1)) + ) + (catch_all + (try + (do + (throw $e-i32 (i32.const 2)) + ) + (catch $e-i32 + (drop (pop i32)) + (rethrow 1) ;; rethrow (i32.const 1) + ) + ) ) ) ) + (catch $e-i32 + (pop i32) ;; result is (i32.const 1) + ) ) ) - (func (export "call_rethrow_br_on_exn") (result i32) (local $exn exnref) + ;; Tests whether the exception stack is managed correctly after rethrows + (func (export "rethrow_depth_test3") (result i32) (try (result i32) (do - (call $try_call_rethrow) - (unreachable) - ) - (catch - (local.set $exn (pop exnref)) - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e-i32 (local.get $exn)) + (try + (do + (try + (do + (throw $e-i32 (i32.const 1)) + ) + (catch_all + (try + (do + (throw $e-i32 (i32.const 2)) + ) + (catch $e-i32 + (drop (pop i32)) + (rethrow 1) ;; rethrow (i32.const 1) + ) + ) + ) + ) + ) + (catch $e-i32 + (rethrow 0) ;; rethrow (i32.const 1) again ) ) ) + (catch $e-i32 + (pop i32) ;; result is (i32.const 1) + ) ) ) ) (assert_trap (invoke "throw_single_value")) (assert_trap (invoke "throw_multiple_values")) -(assert_trap (invoke "rethrow_null")) (assert_return (invoke "try_nothrow") (i32.const 3)) (assert_return (invoke "try_throw_catch") (i32.const 3)) -(assert_return (invoke "try_call_catch") (i32.const 3)) +(assert_trap (invoke "try_throw_nocatch")) +(assert_return (invoke "try_throw_catchall") (i32.const 3)) +(assert_return (invoke "try_call_catch") (i32.const 5)) +(assert_return (invoke "try_throw_multivalue_catch") (i32.const 5)) (assert_trap (invoke "try_throw_rethrow")) (assert_trap (invoke "try_call_rethrow")) -(assert_trap (invoke "br_on_exn_null")) -(assert_return (invoke "br_on_exn_match_no_value")) -(assert_return (invoke "br_on_exn_match_single_value") (i32.const 5)) -(assert_return (invoke "br_on_exn_match_multiple_values") (tuple.make (i32.const 3) (f32.const 3.5))) -(assert_trap (invoke "br_on_exn_dont_match")) -(assert_return (invoke "call_rethrow_br_on_exn") (i32.const 5)) +(assert_return (invoke "rethrow_depth_test1") (i32.const 2)) +(assert_return (invoke "rethrow_depth_test2") (i32.const 1)) +(assert_return (invoke "rethrow_depth_test3") (i32.const 1)) (assert_invalid (module (func $f0 (try (do (nop)) - (catch (i32.const 0)) + (catch $e-i32 + (pop i32) + ) ) ) ) @@ -215,7 +235,9 @@ (func $f0 (try (do (i32.const 0)) - (catch (i32.const 0)) + (catch $e-i32 + (pop i32) + ) ) ) ) @@ -244,15 +266,6 @@ (assert_invalid (module - (func $f0 - (rethrow (i32.const 0)) - ) - ) - "rethrow's argument must be exnref type" -) - -(assert_invalid - (module (event $e-i32 (attr 0) (param i32)) (func $f0 (result i32) (block $l0 (result i32) |