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> {
break;
}
case kExprCatch: {
// TODO(kschimpf): Fix to use type signature of exception.
CHECK_PROTOTYPE_OPCODE(eh);
ExceptionIndexImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break;
len = 1 + imm.length;
if (!VALIDATE(!control_.empty())) {
this->error("catch does not match any try");
break;
}
Control* c = &control_.back();
if (!VALIDATE(c->is_try())) {
this->error("catch does not match any try");
break;
}
if (!VALIDATE(c->is_incomplete_try())) {
OPCODE_ERROR(opcode, "multiple catch blocks not implemented");
if (!VALIDATE(!c->is_try_catchall())) {
this->error("catch after catch-all for try");
break;
}
c->kind = kControlTryCatch;
......@@ -1554,7 +1549,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
FallThruTo(c);
stack_.resize(c->stack_depth);
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
// TODO(mstarzinger): Implement control flow for catch-all.
break;
}
case kExprLoop: {
......
......@@ -103,14 +103,15 @@ function assertWasmThrows(instance, runtime_id, values, code) {
assertEquals(42, instance.exports.simple_throw_catch_to_0_1(1));
})();
// Test that we can distinguish which exception was thrown.
(function TestCatchComplex() {
// Test that we can distinguish which exception was thrown by using a cascaded
// sequence of nested try blocks with a single catch block each.
(function TestCatchComplex1() {
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_different_exceptions", kSig_i_i)
builder.addFunction("catch_complex", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprTry, kWasmI32,
......@@ -134,13 +135,52 @@ function assertWasmThrows(instance, runtime_id, values, code) {
kExprEnd,
kExprCatch, except2,
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();
let instance = builder.instantiate();
assertEquals(3, instance.exports.catch_different_exceptions(0));
assertEquals(4, instance.exports.catch_different_exceptions(1));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_different_exceptions(2));
assertEquals(3, instance.exports.catch_complex(0));
assertEquals(4, instance.exports.catch_complex(1));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_complex(2));
})();
// Test throwing an exception with multiple values.
......
......@@ -2438,12 +2438,10 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
byte ex1 = 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), WASM_CATCH(ex2), kExprEnd);
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_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) {
......@@ -2451,8 +2449,11 @@ TEST_F(FunctionBodyDecoderTest, TryCatchAll) {
TestModuleBuilder builder;
module = builder.module();
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, 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, WASM_CATCH(ex1), kExprEnd);
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