diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index c1dbfe3094..2e0f268193 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -944,7 +944,8 @@ enum ControlKind : uint8_t { kControlLet, kControlTry, kControlTryCatch, - kControlTryCatchAll + kControlTryCatchAll, + kControlTryUnwind }; enum Reachability : uint8_t { @@ -1004,8 +1005,10 @@ struct ControlBase : public PcForErrors { bool is_incomplete_try() const { return kind == kControlTry; } bool is_try_catch() const { return kind == kControlTryCatch; } bool is_try_catchall() const { return kind == kControlTryCatchAll; } + bool is_try_unwind() const { return kind == kControlTryUnwind; } bool is_try() const { - return is_incomplete_try() || is_try_catch() || is_try_catchall(); + return is_incomplete_try() || is_try_catch() || is_try_catchall() || + is_try_unwind(); } inline Merge* br_merge() { @@ -2402,6 +2405,7 @@ class WasmFullDecoder : public WasmDecoder { case kControlIfElse: case kControlTryCatch: case kControlTryCatchAll: + case kControlTryUnwind: case kControlLet: // TODO(7748): Implement break; } @@ -2520,6 +2524,10 @@ class WasmFullDecoder : public WasmDecoder { this->DecodeError("catch after catch-all for try"); return 0; } + if (!VALIDATE(!c->is_try_unwind())) { + this->DecodeError("catch after unwind for try"); + return 0; + } c->kind = kControlTryCatch; FallThruTo(c); DCHECK_LE(stack_ + c->stack_depth, stack_end_); @@ -2553,7 +2561,8 @@ class WasmFullDecoder : public WasmDecoder { "delegate target must be a try block or the function block"); return 0; } - if (target->is_try_catch() || target->is_try_catchall()) { + if (target->is_try_catch() || target->is_try_catchall() || + target->is_try_catchall()) { this->DecodeError( "cannot delegate inside the catch handler of the target"); } @@ -2573,12 +2582,13 @@ class WasmFullDecoder : public WasmDecoder { this->DecodeError("unwind does not match a try"); return 0; } - if (!VALIDATE(!c->is_try_catch() && !c->is_try_catchall())) { + if (!VALIDATE(!c->is_try_catch() && !c->is_try_catchall() && + !c->is_try_unwind())) { this->error("catch, catch-all or unwind already present for try"); return 0; } - c->kind = kControlTryCatchAll; FallThruTo(c); + c->kind = kControlTryUnwind; stack_end_ = stack_ + c->stack_depth; c->reachability = control_at(1)->innerReachability(); CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c); @@ -2704,7 +2714,11 @@ class WasmFullDecoder : public WasmDecoder { CHECK_PROTOTYPE_OPCODE(eh); DCHECK(c->is_try()); if (!VALIDATE(!c->is_try_catchall())) { - this->error("catch-all or unwind already present for try"); + this->error("catch-all already present for try"); + return 0; + } + if (!VALIDATE(!c->is_try_unwind())) { + this->error("cannot have a catch-all after unwind"); return 0; } c->kind = kControlTryCatchAll; @@ -2741,6 +2755,11 @@ class WasmFullDecoder : public WasmDecoder { CALL_INTERFACE_IF_REACHABLE(Rethrow, c); EndControl(); } + if (c->is_try_unwind()) { + // Unwind implicitly rethrows at the end. + CALL_INTERFACE_IF_REACHABLE(Rethrow, c); + EndControl(); + } if (c->is_let()) { this->local_types_.erase(this->local_types_.begin(), diff --git a/src/wasm/graph-builder-interface.cc b/src/wasm/graph-builder-interface.cc index ce5ee95947..52456417e3 100644 --- a/src/wasm/graph-builder-interface.cc +++ b/src/wasm/graph-builder-interface.cc @@ -660,8 +660,10 @@ class WasmGraphBuildingInterface { } void Rethrow(FullDecoder* decoder, Control* block) { - DCHECK(block->is_try_catchall() || block->is_try_catch()); + DCHECK(block->is_try_catchall() || block->is_try_catch() || + block->is_try_unwind()); TFNode* exception = block->try_info->exception; + DCHECK_NOT_NULL(exception); BUILD(Rethrow, exception); TerminateThrow(decoder); } @@ -747,7 +749,8 @@ class WasmGraphBuildingInterface { } void CatchAll(FullDecoder* decoder, Control* block) { - DCHECK(block->is_try_catchall() || block->is_try_catch()); + DCHECK(block->is_try_catchall() || block->is_try_catch() || + block->is_try_unwind()); DCHECK_EQ(decoder->control_at(0), block); current_catch_ = block->previous_catch; // Pop try scope. diff --git a/test/mjsunit/wasm/exceptions-rethrow.js b/test/mjsunit/wasm/exceptions-rethrow.js index 5b3e1fdc15..6f1fd773c5 100644 --- a/test/mjsunit/wasm/exceptions-rethrow.js +++ b/test/mjsunit/wasm/exceptions-rethrow.js @@ -53,14 +53,6 @@ load("test/mjsunit/wasm/exceptions-utils.js"); kExprRethrow, 0, kExprEnd, ]).exportFunc(); - builder.addFunction("rethrow0_unwind", kSig_v_v) - .addBody([ - kExprTry, kWasmStmt, - kExprThrow, except, - kExprUnwind, - kExprRethrow, 0, - kExprEnd, - ]).exportFunc(); builder.addFunction("rethrow1", kSig_i_i) .addBody([ kExprTry, kWasmI32, @@ -74,27 +66,11 @@ load("test/mjsunit/wasm/exceptions-utils.js"); kExprI32Const, 23, kExprEnd ]).exportFunc(); - builder.addFunction("rethrow1_unwind", kSig_i_i) - .addBody([ - kExprTry, kWasmI32, - kExprThrow, except, - kExprUnwind, - kExprLocalGet, 0, - kExprI32Eqz, - kExprIf, kWasmStmt, - kExprRethrow, 1, - kExprEnd, - kExprI32Const, 23, - kExprEnd - ]).exportFunc(); let instance = builder.instantiate(); assertWasmThrows(instance, except, [], () => instance.exports.rethrow0()); - assertWasmThrows(instance, except, [], () => instance.exports.rethrow0_unwind()); assertWasmThrows(instance, except, [], () => instance.exports.rethrow1(0)); - assertWasmThrows(instance, except, [], () => instance.exports.rethrow1_unwind(0)); assertEquals(23, instance.exports.rethrow1(1)); - assertEquals(23, instance.exports.rethrow1_unwind(1)); })(); // Test that rethrow expression properly target the correct surrounding try diff --git a/test/mjsunit/wasm/exceptions.js b/test/mjsunit/wasm/exceptions.js index 29c4dfb1bd..a5bc9a9d51 100644 --- a/test/mjsunit/wasm/exceptions.js +++ b/test/mjsunit/wasm/exceptions.js @@ -215,7 +215,7 @@ load("test/mjsunit/wasm/exceptions-utils.js"); let instance = builder.instantiate({imp: {ort: throw_exc}}); assertEquals(11, instance.exports.call_import()); - assertEquals(11, instance.exports.call_import_unwind()); + assertThrows(instance.exports.call_import_unwind, WebAssembly.RuntimeError, "My user text"); })(); (function TestExnWithWasmProtoNotCaught() { diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc index ea867bda50..b4ca0fd7c2 100644 --- a/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/test/unittests/wasm/function-body-decoder-unittest.cc @@ -2895,9 +2895,16 @@ TEST_F(FunctionBodyDecoderTest, Rethrow) { WASM_FEATURE_SCOPE(eh); ExpectValidates(sigs.v_v(), {WASM_TRY_OP, kExprElse, kExprRethrow, 0, kExprEnd}); - ExpectFailure(sigs.v_v(), {WASM_TRY_OP, kExprRethrow, kExprCatch, kExprEnd}); - ExpectFailure(sigs.v_v(), {WASM_BLOCK(kExprRethrow)}); - ExpectFailure(sigs.v_v(), {kExprRethrow}); + ExpectFailure(sigs.v_v(), + {WASM_TRY_OP, kExprRethrow, 0, kExprCatch, kExprEnd}, + kAppendEnd, "rethrow not targeting catch or catch-all"); + ExpectFailure(sigs.v_v(), {WASM_BLOCK(kExprRethrow, 0)}, kAppendEnd, + "rethrow not targeting catch or catch-all"); + ExpectFailure(sigs.v_v(), {kExprRethrow, 0}, kAppendEnd, + "rethrow not targeting catch or catch-all"); + ExpectFailure(sigs.v_v(), + {WASM_TRY_OP, kExprUnwind, kExprRethrow, 0, kExprEnd}, + kAppendEnd, "rethrow not targeting catch or catch-all"); } TEST_F(FunctionBodyDecoderTest, TryDelegate) {