Commit 2e007737 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Add support for multiple catch blocks.

This adds support for multiple catch blocks being attached to a single
try block. The implemented semantics are that type checks are performed
in order from top to bottom.

Note that multiple catch blocks of the same type are not prohibited and
will be accepted, making the second such block essentially unreachable.
The current proposal neither explicitly allows nor prohibits it.

R=clemensh@chromium.org
TEST=mjsunit/wasm/exceptions
BUG=v8:8091

Change-Id: I31e7a07a7cffdd909a58342e00f05e52ed1a3182
Reviewed-on: https://chromium-review.googlesource.com/c/1270591Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56478}
parent f8375f48
...@@ -1500,26 +1500,21 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1500,26 +1500,21 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break; break;
} }
case kExprCatch: { case kExprCatch: {
// TODO(kschimpf): Fix to use type signature of exception.
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
ExceptionIndexImmediate<validate> imm(this, this->pc_); ExceptionIndexImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break; if (!this->Validate(this->pc_, imm)) break;
len = 1 + imm.length;
if (!VALIDATE(!control_.empty())) { if (!VALIDATE(!control_.empty())) {
this->error("catch does not match any try"); this->error("catch does not match any try");
break; break;
} }
Control* c = &control_.back(); Control* c = &control_.back();
if (!VALIDATE(c->is_try())) { if (!VALIDATE(c->is_try())) {
this->error("catch does not match any try"); this->error("catch does not match any try");
break; break;
} }
if (!VALIDATE(!c->is_try_catchall())) {
if (!VALIDATE(c->is_incomplete_try())) { this->error("catch after catch-all for try");
OPCODE_ERROR(opcode, "multiple catch blocks not implemented");
break; break;
} }
c->kind = kControlTryCatch; c->kind = kControlTryCatch;
...@@ -1554,7 +1549,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1554,7 +1549,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
FallThruTo(c); FallThruTo(c);
stack_.resize(c->stack_depth); stack_.resize(c->stack_depth);
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c); CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
// TODO(mstarzinger): Implement control flow for catch-all.
break; break;
} }
case kExprLoop: { case kExprLoop: {
......
...@@ -103,14 +103,15 @@ function assertWasmThrows(instance, runtime_id, values, code) { ...@@ -103,14 +103,15 @@ function assertWasmThrows(instance, runtime_id, values, code) {
assertEquals(42, instance.exports.simple_throw_catch_to_0_1(1)); assertEquals(42, instance.exports.simple_throw_catch_to_0_1(1));
})(); })();
// Test that we can distinguish which exception was thrown. // Test that we can distinguish which exception was thrown by using a cascaded
(function TestCatchComplex() { // sequence of nested try blocks with a single catch block each.
(function TestCatchComplex1() {
print(arguments.callee.name); print(arguments.callee.name);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v); let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v); let except2 = builder.addException(kSig_v_v);
let except3 = builder.addException(kSig_v_v); let except3 = builder.addException(kSig_v_v);
builder.addFunction("catch_different_exceptions", kSig_i_i) builder.addFunction("catch_complex", kSig_i_i)
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprTry, kWasmI32, kExprTry, kWasmI32,
...@@ -134,13 +135,52 @@ function assertWasmThrows(instance, runtime_id, values, code) { ...@@ -134,13 +135,52 @@ function assertWasmThrows(instance, runtime_id, values, code) {
kExprEnd, kExprEnd,
kExprCatch, except2, kExprCatch, except2,
kExprI32Const, 4, kExprI32Const, 4,
kExprEnd kExprEnd,
]).exportFunc();
let instance = builder.instantiate();
assertEquals(3, instance.exports.catch_complex(0));
assertEquals(4, instance.exports.catch_complex(1));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_complex(2));
})();
// Test that we can distinguish which exception was thrown by using a single
// try block with multiple associated catch blocks in sequence.
(function TestCatchComplex2() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v);
let except3 = builder.addException(kSig_v_v);
builder.addFunction("catch_complex", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprThrow, except1,
kExprElse,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprThrow, except2,
kExprElse,
kExprThrow, except3,
kExprEnd,
kExprEnd,
kExprI32Const, 2,
kExprCatch, except1,
kExprI32Const, 3,
kExprCatch, except2,
kExprI32Const, 4,
kExprEnd,
]).exportFunc(); ]).exportFunc();
let instance = builder.instantiate(); let instance = builder.instantiate();
assertEquals(3, instance.exports.catch_different_exceptions(0)); assertEquals(3, instance.exports.catch_complex(0));
assertEquals(4, instance.exports.catch_different_exceptions(1)); assertEquals(4, instance.exports.catch_complex(1));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_different_exceptions(2)); assertWasmThrows(instance, except3, [], () => instance.exports.catch_complex(2));
})(); })();
// Test throwing an exception with multiple values. // Test throwing an exception with multiple values.
......
...@@ -2438,12 +2438,10 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) { ...@@ -2438,12 +2438,10 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
byte ex1 = builder.AddException(sigs.v_v()); byte ex1 = builder.AddException(sigs.v_v());
byte ex2 = builder.AddException(sigs.v_v()); byte ex2 = builder.AddException(sigs.v_v());
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_CATCH(ex2), kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); // Missing catch. EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); // Missing catch.
EXPECT_FAILURE(v_v, WASM_TRY_OP, WASM_CATCH(ex1)); // Missing end. EXPECT_FAILURE(v_v, WASM_TRY_OP, WASM_CATCH(ex1)); // Missing end.
EXPECT_FAILURE(v_v, WASM_CATCH(ex1), kExprEnd); // Missing try. EXPECT_FAILURE(v_v, WASM_CATCH(ex1), kExprEnd); // Missing try.
// TODO(mstarzinger): Double catch. Fix this to verify.
EXPECT_FAILURE(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_CATCH(ex2), kExprEnd);
} }
TEST_F(FunctionBodyDecoderTest, TryCatchAll) { TEST_F(FunctionBodyDecoderTest, TryCatchAll) {
...@@ -2451,8 +2449,11 @@ TEST_F(FunctionBodyDecoderTest, TryCatchAll) { ...@@ -2451,8 +2449,11 @@ TEST_F(FunctionBodyDecoderTest, TryCatchAll) {
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte ex1 = builder.AddException(sigs.v_v()); byte ex1 = builder.AddException(sigs.v_v());
byte ex2 = builder.AddException(sigs.v_v());
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprCatchAll, kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprCatchAll, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_CATCH(ex2),
kExprCatchAll, kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, kExprCatchAll, kExprEnd); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, kExprCatchAll, kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, WASM_CATCH(ex1), kExprEnd); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, WASM_CATCH(ex1), kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll); // Missing end. EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll); // Missing end.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment