[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/1270591
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56478}
This commit is contained in:
Michael Starzinger 2018-10-09 13:16:52 +02:00 committed by Commit Bot
parent f8375f480e
commit 2e007737ca
3 changed files with 54 additions and 19 deletions

View File

@ -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: {

View File

@ -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_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 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_complex(0));
assertEquals(4, instance.exports.catch_complex(1));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_complex(2));
})();
// Test throwing an exception with multiple values.

View File

@ -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.