[wasm][eh] Implicit rethrow after unwind
Implicitly rethrow the exception when we reach the end of a try..unwind..end. Also make it a validation error to rethrow an exception caught by an unwind block. R=clemensb@chromium.org Bug: v8:8091 Change-Id: Ia149d2e81b1fbfa9209047b35ff0c9fedc1b8895 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2696662 Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#72785}
This commit is contained in:
parent
a326829e9a
commit
469788dff6
@ -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<validate> {
|
||||
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<Value>* br_merge() {
|
||||
@ -2402,6 +2405,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
case kControlIfElse:
|
||||
case kControlTryCatch:
|
||||
case kControlTryCatchAll:
|
||||
case kControlTryUnwind:
|
||||
case kControlLet: // TODO(7748): Implement
|
||||
break;
|
||||
}
|
||||
@ -2520,6 +2524,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
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<validate> {
|
||||
"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<validate> {
|
||||
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<validate> {
|
||||
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<validate> {
|
||||
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(),
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user