Commit eac44d60 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Initial implementation of rethrow expressions.

R=titzer@chromium.org
TEST=mjsunit/wasm/exceptions-rethrow,unittests/FunctionBodyDecoderTest
BUG=v8:8091

Change-Id: If52be505fb9897af1bd59d17d1ab47b33b665be0
Reviewed-on: https://chromium-review.googlesource.com/c/1273050
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56563}
parent fad1c1c9
......@@ -1471,9 +1471,17 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
}
case kExprRethrow: {
// TODO(kschimpf): Implement.
CHECK_PROTOTYPE_OPCODE(eh);
OPCODE_ERROR(opcode, "not implemented yet");
BreakDepthImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm, control_.size())) break;
Control* c = control_at(imm.depth);
if (!VALIDATE(c->is_try_catchall() || c->is_try_catch())) {
this->error("rethrow not targeting catch or catch-all");
break;
}
CALL_INTERFACE_IF_REACHABLE(Rethrow, c);
len = 1 + imm.length;
EndControl();
break;
}
case kExprThrow: {
......
......@@ -419,6 +419,7 @@ class WasmGraphBuildingInterface {
}
void Rethrow(FullDecoder* decoder, Control* block) {
DCHECK(block->is_try_catchall() || block->is_try_catch());
TFNode* exception = block->try_info->exception;
BUILD(Rethrow, exception);
Unreachable(decoder);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --experimental-wasm-eh --allow-natives-syntax
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function assertWasmThrows(instance, runtime_id, code) {
try {
if (typeof code === 'function') {
code();
} else {
eval(code);
}
} catch (e) {
assertInstanceof(e, WebAssembly.RuntimeError);
var e_runtime_id = %GetWasmExceptionId(e, instance);
assertTrue(Number.isInteger(e_runtime_id));
assertEquals(e_runtime_id, runtime_id);
return; // Success.
}
throw new MjsUnitAssertionError('Did not throw <' + runtime_id + '>');
}
// Test that rethrow expressions can target catch blocks.
(function TestRethrowInCatch() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addFunction("rethrow0", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprCatch, except,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
builder.addFunction("rethrow1", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprCatch, except,
kExprGetLocal, 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.rethrow1(0));
assertEquals(23, instance.exports.rethrow1(1));
})();
// Test that rethrow expressions can target catch-all blocks.
(function TestRethrowInCatchAll() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addFunction("rethrow0", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except,
kExprCatchAll,
kExprRethrow, 0,
kExprEnd,
]).exportFunc();
builder.addFunction("rethrow1", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprCatchAll,
kExprGetLocal, 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.rethrow1(0));
assertEquals(23, instance.exports.rethrow1(1));
})();
// Test that rethrow expression properly target the correct surrounding try
// block even in the presence of multiple handlers being involved.
(function TestRethrowNested() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v);
builder.addFunction("rethrow_nested", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except2,
kExprCatch, except2,
kExprTry, kWasmI32,
kExprThrow, except1,
kExprCatch, except1,
kExprGetLocal, 0,
kExprI32Const, 0,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprRethrow, 1,
kExprEnd,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprRethrow, 2,
kExprEnd,
kExprI32Const, 23,
kExprEnd,
kExprEnd,
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(instance, except1, () => instance.exports.rethrow_nested(0));
assertWasmThrows(instance, except2, () => instance.exports.rethrow_nested(1));
assertEquals(23, instance.exports.rethrow_nested(2));
})();
// Test that an exception being rethrow can be caught by another local catch
// block in the same function without ever unwinding the activation.
(function TestRethrowRecatch() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addFunction("rethrow_recatch", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprThrow, except,
kExprCatch, except,
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprRethrow, 2,
kExprEnd,
kExprI32Const, 42,
kExprCatch, except,
kExprI32Const, 23,
kExprEnd,
kExprEnd,
]).exportFunc();
let instance = builder.instantiate();
assertEquals(23, instance.exports.rethrow_recatch(0));
assertEquals(42, instance.exports.rethrow_recatch(1));
})();
......@@ -172,6 +172,7 @@ let kExprElse = 0x05;
let kExprTry = 0x06;
let kExprCatch = 0x07;
let kExprThrow = 0x08;
let kExprRethrow = 0x09;
let kExprCatchAll = 0x0a;
let kExprEnd = 0x0b;
let kExprBr = 0x0c;
......
......@@ -2430,6 +2430,7 @@ TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) {
#define WASM_TRY_OP kExprTry, kLocalVoid
#define WASM_CATCH(index) kExprCatch, static_cast<byte>(index)
#define WASM_RETHROW(depth) kExprRethrow, static_cast<byte>(depth)
TEST_F(FunctionBodyDecoderTest, TryCatch) {
WASM_FEATURE_SCOPE(eh);
......@@ -2460,8 +2461,26 @@ TEST_F(FunctionBodyDecoderTest, TryCatchAll) {
EXPECT_FAILURE(v_v, kExprCatchAll, kExprEnd); // Missing try.
}
TEST_F(FunctionBodyDecoderTest, Rethrow) {
WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder;
module = builder.module();
byte ex1 = builder.AddException(sigs.v_v());
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_RETHROW(0), kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, WASM_RETHROW(0), kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, WASM_BLOCK(WASM_RETHROW(1)),
kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, WASM_BLOCK(WASM_RETHROW(0)),
kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, WASM_RETHROW(23), kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, WASM_RETHROW(0), kExprCatchAll, kExprEnd);
EXPECT_FAILURE(v_v, WASM_BLOCK(WASM_RETHROW(0)));
EXPECT_FAILURE(v_v, WASM_RETHROW(0));
}
#undef WASM_TRY_OP
#undef WASM_CATCH
#undef WASM_RETHROW
TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
WASM_FEATURE_SCOPE(mv);
......
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