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

[wasm] Switch to new 'catch' and 'br_on_exn' proposal.

This switches the experimental exception handling implementation to the
new proposal where 'catch' blocks behave in a catch-all fashion and a
new 'br_on_exn' operation is used to check for a certain exception type
and extract the exception values on a match.

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

Change-Id: Ib12ba28b3aa2a7d831312a83abcb00bf56d0adc3
Reviewed-on: https://chromium-review.googlesource.com/c/1409431
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58832}
parent bc3d7298
...@@ -480,6 +480,10 @@ class LiftoffCompiler { ...@@ -480,6 +480,10 @@ class LiftoffCompiler {
unsupported(decoder, "try"); unsupported(decoder, "try");
} }
void Catch(FullDecoder* decoder, Control* block, Value* exception) {
unsupported(decoder, "catch");
}
void If(FullDecoder* decoder, const Value& cond, Control* if_block) { void If(FullDecoder* decoder, const Value& cond, Control* if_block) {
DCHECK_EQ(if_block, decoder->control_at(0)); DCHECK_EQ(if_block, decoder->control_at(0));
DCHECK(if_block->is_if()); DCHECK(if_block->is_if());
...@@ -1860,16 +1864,13 @@ class LiftoffCompiler { ...@@ -1860,16 +1864,13 @@ class LiftoffCompiler {
const Vector<Value>& args) { const Vector<Value>& args) {
unsupported(decoder, "throw"); unsupported(decoder, "throw");
} }
void Rethrow(FullDecoder* decoder, Control* block) { void Rethrow(FullDecoder* decoder, const Value& exception) {
unsupported(decoder, "rethrow"); unsupported(decoder, "rethrow");
} }
void CatchException(FullDecoder* decoder, void BrOnException(FullDecoder* decoder, const Value& exception,
const ExceptionIndexImmediate<validate>& imm, const ExceptionIndexImmediate<validate>& imm,
Control* block, Vector<Value> caught_values) { uint32_t depth, Vector<Value> values) {
unsupported(decoder, "catch"); unsupported(decoder, "br_on_exn");
}
void CatchAll(FullDecoder* decoder, Control* block) {
unsupported(decoder, "catch-all");
} }
void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args, void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
const MemoryAccessImmediate<validate>& imm, Value* result) { const MemoryAccessImmediate<validate>& imm, Value* result) {
......
...@@ -546,8 +546,7 @@ enum ControlKind : uint8_t { ...@@ -546,8 +546,7 @@ enum ControlKind : uint8_t {
kControlBlock, kControlBlock,
kControlLoop, kControlLoop,
kControlTry, kControlTry,
kControlTryCatch, kControlTryCatch
kControlTryCatchAll
}; };
enum Reachability : uint8_t { enum Reachability : uint8_t {
...@@ -602,10 +601,7 @@ struct ControlBase { ...@@ -602,10 +601,7 @@ struct ControlBase {
bool is_loop() const { return kind == kControlLoop; } bool is_loop() const { return kind == kControlLoop; }
bool is_incomplete_try() const { return kind == kControlTry; } bool is_incomplete_try() const { return kind == kControlTry; }
bool is_try_catch() const { return kind == kControlTryCatch; } bool is_try_catch() const { return kind == kControlTryCatch; }
bool is_try_catchall() const { return kind == kControlTryCatchAll; } bool is_try() const { return is_incomplete_try() || is_try_catch(); }
bool is_try() const {
return is_incomplete_try() || is_try_catch() || is_try_catchall();
}
inline Merge<Value>* br_merge() { inline Merge<Value>* br_merge() {
return is_loop() ? &this->start_merge : &this->end_merge; return is_loop() ? &this->start_merge : &this->end_merge;
...@@ -626,6 +622,7 @@ struct ControlBase { ...@@ -626,6 +622,7 @@ struct ControlBase {
F(Block, Control* block) \ F(Block, Control* block) \
F(Loop, Control* block) \ F(Loop, Control* block) \
F(Try, Control* block) \ F(Try, Control* block) \
F(Catch, Control* block, Value* exception) \
F(If, const Value& cond, Control* if_block) \ F(If, const Value& cond, Control* if_block) \
F(FallThruTo, Control* c) \ F(FallThruTo, Control* c) \
F(PopControl, Control* block) \ F(PopControl, Control* block) \
...@@ -674,10 +671,10 @@ struct ControlBase { ...@@ -674,10 +671,10 @@ struct ControlBase {
const Value& input0, const Value& input1, Value* result) \ const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexImmediate<validate>& imm, \ F(Throw, const ExceptionIndexImmediate<validate>& imm, \
const Vector<Value>& args) \ const Vector<Value>& args) \
F(Rethrow, Control* block) \ F(Rethrow, const Value& exception) \
F(CatchException, const ExceptionIndexImmediate<validate>& imm, \ F(BrOnException, const Value& exception, \
Control* block, Vector<Value> caught_values) \ const ExceptionIndexImmediate<validate>& imm, uint32_t depth, \
F(CatchAll, Control* block) \ Vector<Value> values) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \ F(AtomicOp, WasmOpcode opcode, Vector<Value> args, \
const MemoryAccessImmediate<validate>& imm, Value* result) \ const MemoryAccessImmediate<validate>& imm, Value* result) \
F(MemoryInit, const MemoryInitImmediate<validate>& imm, const Value& dst, \ F(MemoryInit, const MemoryInitImmediate<validate>& imm, const Value& dst, \
...@@ -1087,7 +1084,6 @@ class WasmDecoder : public Decoder { ...@@ -1087,7 +1084,6 @@ class WasmDecoder : public Decoder {
MemoryAccessImmediate<validate> imm(decoder, pc, UINT32_MAX); MemoryAccessImmediate<validate> imm(decoder, pc, UINT32_MAX);
return 1 + imm.length; return 1 + imm.length;
} }
case kExprRethrow:
case kExprBr: case kExprBr:
case kExprBrIf: { case kExprBrIf: {
BranchDepthImmediate<validate> imm(decoder, pc); BranchDepthImmediate<validate> imm(decoder, pc);
...@@ -1116,12 +1112,17 @@ class WasmDecoder : public Decoder { ...@@ -1116,12 +1112,17 @@ class WasmDecoder : public Decoder {
return 1 + imm.length; return 1 + imm.length;
} }
case kExprThrow: case kExprThrow: {
case kExprCatch: {
ExceptionIndexImmediate<validate> imm(decoder, pc); ExceptionIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length; return 1 + imm.length;
} }
case kExprBrOnExn: {
BranchDepthImmediate<validate> imm_br(decoder, pc);
ExceptionIndexImmediate<validate> imm_idx(decoder, pc + imm_br.length);
return 1 + imm_br.length + imm_idx.length;
}
case kExprSetLocal: case kExprSetLocal:
case kExprTeeLocal: case kExprTeeLocal:
case kExprGetLocal: { case kExprGetLocal: {
...@@ -1584,15 +1585,8 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1584,15 +1585,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
case kExprRethrow: { case kExprRethrow: {
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
BranchDepthImmediate<validate> imm(this, this->pc_); auto exception = Pop(0, kWasmExceptRef);
if (!this->Validate(this->pc_, imm, control_.size())) break; CALL_INTERFACE_IF_REACHABLE(Rethrow, exception);
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(); EndControl();
break; break;
} }
...@@ -1620,9 +1614,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1620,9 +1614,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
case kExprCatch: { case kExprCatch: {
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
ExceptionIndexImmediate<validate> imm(this, this->pc_);
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;
...@@ -1632,43 +1623,45 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1632,43 +1623,45 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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"); this->error("catch already present for try");
break; break;
} }
c->kind = kControlTryCatch; c->kind = kControlTryCatch;
FallThruTo(c); FallThruTo(c);
stack_.erase(stack_.begin() + c->stack_depth, stack_.end()); stack_.erase(stack_.begin() + c->stack_depth, stack_.end());
const WasmExceptionSig* sig = imm.exception->sig;
for (size_t i = 0, e = sig->parameter_count(); i < e; ++i) {
Push(sig->GetParam(i));
}
Vector<Value> values(stack_.data() + c->stack_depth,
sig->parameter_count());
c->reachability = control_at(1)->innerReachability(); c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchException, imm, c, values); auto* exception = Push(kWasmExceptRef);
CALL_INTERFACE_IF_PARENT_REACHABLE(Catch, c, exception);
break; break;
} }
case kExprCatchAll: { case kExprBrOnExn: {
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
if (!VALIDATE(!control_.empty())) { BranchDepthImmediate<validate> imm_br(this, this->pc_);
this->error("catch-all does not match any try"); if (!this->Validate(this->pc_, imm_br, control_.size())) break;
break; ExceptionIndexImmediate<validate> imm_idx(this,
} this->pc_ + imm_br.length);
Control* c = &control_.back(); if (!this->Validate(this->pc_ + imm_br.length, imm_idx)) break;
if (!VALIDATE(c->is_try())) { Control* c = control_at(imm_br.depth);
this->error("catch-all does not match any try"); auto exception = Pop(0, kWasmExceptRef);
break; const WasmExceptionSig* sig = imm_idx.exception->sig;
} size_t value_count = sig->parameter_count();
if (!VALIDATE(!c->is_try_catchall())) { // TODO(mstarzinger): This operand stack mutation is an ugly hack to
this->error("catch-all already present for try"); // make both type checking here as well as environment merging in the
break; // graph builder interface work out of the box. We should introduce
// special handling for both and do minimal/no stack mutation here.
for (size_t i = 0; i < value_count; ++i) Push(sig->GetParam(i));
Vector<Value> values(stack_.data() + c->stack_depth, value_count);
if (!TypeCheckBranch(c)) break;
if (control_.back().reachable()) {
CALL_INTERFACE(BrOnException, exception, imm_idx, imm_br.depth,
values);
c->br_merge()->reached = true;
} }
c->kind = kControlTryCatchAll; len = 1 + imm_br.length + imm_idx.length;
FallThruTo(c); for (size_t i = 0; i < value_count; ++i) Pop();
stack_.erase(stack_.begin() + c->stack_depth, stack_.end()); auto* pexception = Push(kWasmExceptRef);
c->reachability = control_at(1)->innerReachability(); *pexception = exception;
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
break; break;
} }
case kExprLoop: { case kExprLoop: {
...@@ -1735,14 +1728,6 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1735,14 +1728,6 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break; break;
} }
} }
if (c->is_try_catch()) {
// Emulate catch-all + re-throw.
FallThruTo(c);
c->reachability = control_at(1)->innerReachability();
CALL_INTERFACE_IF_PARENT_REACHABLE(CatchAll, c);
CALL_INTERFACE_IF_REACHABLE(Rethrow, c);
EndControl();
}
if (!TypeCheckFallThru(c)) break; if (!TypeCheckFallThru(c)) break;
......
...@@ -153,8 +153,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, ...@@ -153,8 +153,7 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
WasmOpcode opcode = i.current(); WasmOpcode opcode = i.current();
if (line_numbers) line_numbers->push_back(i.position()); if (line_numbers) line_numbers->push_back(i.position());
if (opcode == kExprElse || opcode == kExprCatch || if (opcode == kExprElse || opcode == kExprCatch) {
opcode == kExprCatchAll) {
control_depth--; control_depth--;
} }
...@@ -197,7 +196,6 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, ...@@ -197,7 +196,6 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
switch (opcode) { switch (opcode) {
case kExprElse: case kExprElse:
case kExprCatch: case kExprCatch:
case kExprCatchAll:
os << " // @" << i.pc_offset(); os << " // @" << i.pc_offset();
control_depth++; control_depth++;
break; break;
......
...@@ -453,62 +453,45 @@ class WasmGraphBuildingInterface { ...@@ -453,62 +453,45 @@ class WasmGraphBuildingInterface {
builder_->TerminateThrow(ssa_env_->effect, ssa_env_->control); builder_->TerminateThrow(ssa_env_->effect, ssa_env_->control);
} }
void Rethrow(FullDecoder* decoder, Control* block) { void Rethrow(FullDecoder* decoder, const Value& exception) {
DCHECK(block->is_try_catchall() || block->is_try_catch()); BUILD(Rethrow, exception.node);
TFNode* exception = block->try_info->exception;
BUILD(Rethrow, exception);
builder_->TerminateThrow(ssa_env_->effect, ssa_env_->control); builder_->TerminateThrow(ssa_env_->effect, ssa_env_->control);
} }
void CatchException(FullDecoder* decoder, void BrOnException(FullDecoder* decoder, const Value& exception,
const ExceptionIndexImmediate<validate>& imm, const ExceptionIndexImmediate<validate>& imm,
Control* block, Vector<Value> values) { uint32_t depth, Vector<Value> values) {
DCHECK(block->is_try_catch()); TFNode* if_match = nullptr;
TFNode* if_no_match = nullptr;
current_catch_ = block->previous_catch; // Pop try scope.
// The catch block is unreachable if no possible throws in the try block
// exist. We only build a landing pad if some node in the try block can
// (possibly) throw. Otherwise the catch environments remain empty.
if (!block->try_info->might_throw()) {
block->reachability = kSpecOnlyReachable;
return;
}
TFNode* exception = block->try_info->exception;
SetEnv(block->try_info->catch_env);
TFNode* if_catch = nullptr;
TFNode* if_no_catch = nullptr;
// Get the exception tag and see if it matches the expected one. // Get the exception tag and see if it matches the expected one.
TFNode* caught_tag = BUILD(GetExceptionTag, exception); TFNode* caught_tag = BUILD(GetExceptionTag, exception.node);
TFNode* exception_tag = BUILD(LoadExceptionTagFromTable, imm.index); TFNode* exception_tag = BUILD(LoadExceptionTagFromTable, imm.index);
TFNode* compare = BUILD(ExceptionTagEqual, caught_tag, exception_tag); TFNode* compare = BUILD(ExceptionTagEqual, caught_tag, exception_tag);
BUILD(BranchNoHint, compare, &if_catch, &if_no_catch); BUILD(BranchNoHint, compare, &if_match, &if_no_match);
SsaEnv* if_no_match_env = Split(decoder, ssa_env_);
// If the tags don't match we continue with the next tag by setting the SsaEnv* if_match_env = Steal(decoder->zone(), ssa_env_);
// false environment as the new {TryInfo::catch_env} here. if_no_match_env->control = if_no_match;
SsaEnv* if_no_catch_env = Split(decoder, ssa_env_); if_match_env->control = if_match;
if_no_catch_env->control = if_no_catch;
SsaEnv* if_catch_env = Steal(decoder->zone(), ssa_env_);
if_catch_env->control = if_catch;
block->try_info->catch_env = if_no_catch_env;
// If the tags match we extract the values from the exception object and // If the tags match we extract the values from the exception object and
// push them onto the operand stack using the passed {values} vector. // push them onto the operand stack using the passed {values} vector.
SetEnv(if_catch_env); SetEnv(if_match_env);
// TODO(mstarzinger): Can't use BUILD() here, GetExceptionValues() returns // TODO(mstarzinger): Can't use BUILD() here, GetExceptionValues() returns
// TFNode** rather than TFNode*. Fix to add landing pads. // TFNode** rather than TFNode*. Fix to add landing pads.
TFNode** caught_values = TFNode** caught_values =
builder_->GetExceptionValues(exception, imm.exception); builder_->GetExceptionValues(exception.node, imm.exception);
for (size_t i = 0, e = values.size(); i < e; ++i) { for (size_t i = 0, e = values.size(); i < e; ++i) {
values[i].node = caught_values[i]; values[i].node = caught_values[i];
} }
BrOrRet(decoder, depth);
// If the tags don't match we fall-through here.
SetEnv(if_no_match_env);
} }
void CatchAll(FullDecoder* decoder, Control* block) { void Catch(FullDecoder* decoder, Control* block, Value* exception) {
DCHECK(block->is_try_catchall() || block->is_try_catch()); DCHECK(block->is_try_catch());
current_catch_ = block->previous_catch; // Pop try scope. current_catch_ = block->previous_catch; // Pop try scope.
...@@ -521,6 +504,8 @@ class WasmGraphBuildingInterface { ...@@ -521,6 +504,8 @@ class WasmGraphBuildingInterface {
} }
SetEnv(block->try_info->catch_env); SetEnv(block->try_info->catch_env);
DCHECK_NOT_NULL(block->try_info->exception);
exception->node = block->try_info->exception;
} }
void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args, void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
......
...@@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE ValueTypes { ...@@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE ValueTypes {
case kWasmF64: case kWasmF64:
return MachineRepresentation::kFloat64; return MachineRepresentation::kFloat64;
case kWasmAnyRef: case kWasmAnyRef:
case kWasmExceptRef:
return MachineRepresentation::kTaggedPointer; return MachineRepresentation::kTaggedPointer;
case kWasmS128: case kWasmS128:
return MachineRepresentation::kSimd128; return MachineRepresentation::kSimd128;
...@@ -335,6 +336,8 @@ class V8_EXPORT_PRIVATE ValueTypes { ...@@ -335,6 +336,8 @@ class V8_EXPORT_PRIVATE ValueTypes {
return "f64"; return "f64";
case kWasmAnyRef: case kWasmAnyRef:
return "ref"; return "ref";
case kWasmExceptRef:
return "exn";
case kWasmS128: case kWasmS128:
return "s128"; return "s128";
case kWasmStmt: case kWasmStmt:
......
...@@ -158,12 +158,12 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -158,12 +158,12 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_I64_OP(StoreMem32, "store32") CASE_I64_OP(StoreMem32, "store32")
CASE_S128_OP(StoreMem, "store128") CASE_S128_OP(StoreMem, "store128")
// Non-standard opcodes. // Exception handling opcodes.
CASE_OP(Try, "try") CASE_OP(Try, "try")
CASE_OP(Catch, "catch")
CASE_OP(Throw, "throw") CASE_OP(Throw, "throw")
CASE_OP(Rethrow, "rethrow") CASE_OP(Rethrow, "rethrow")
CASE_OP(Catch, "catch") CASE_OP(BrOnExn, "br_on_exn")
CASE_OP(CatchAll, "catch_all")
// asm.js-only opcodes. // asm.js-only opcodes.
CASE_F64_OP(Acos, "acos") CASE_F64_OP(Acos, "acos")
......
...@@ -19,22 +19,22 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function); ...@@ -19,22 +19,22 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature); bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
// Control expressions and blocks. // Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \ #define FOREACH_CONTROL_OPCODE(V) \
V(Unreachable, 0x00, _) \ V(Unreachable, 0x00, _) \
V(Nop, 0x01, _) \ V(Nop, 0x01, _) \
V(Block, 0x02, _) \ V(Block, 0x02, _) \
V(Loop, 0x03, _) \ V(Loop, 0x03, _) \
V(If, 0x004, _) \ V(If, 0x04, _) \
V(Else, 0x05, _) \ V(Else, 0x05, _) \
V(Try, 0x06, _ /* eh_prototype */) \ V(Try, 0x06, _ /* eh_prototype */) \
V(Catch, 0x07, _ /* eh_prototype */) \ V(Catch, 0x07, _ /* eh_prototype */) \
V(Throw, 0x08, _ /* eh_prototype */) \ V(Throw, 0x08, _ /* eh_prototype */) \
V(Rethrow, 0x09, _ /* eh_prototype */) \ V(Rethrow, 0x09, _ /* eh_prototype */) \
V(CatchAll, 0x0a, _ /* eh prototype */) \ V(BrOnExn, 0x0a, _ /* eh prototype */) \
V(End, 0x0b, _) \ V(End, 0x0b, _) \
V(Br, 0x0c, _) \ V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \ V(BrIf, 0x0d, _) \
V(BrTable, 0x0e, _) \ V(BrTable, 0x0e, _) \
V(Return, 0x0f, _) V(Return, 0x0f, _)
// Constants, locals, globals, and calls. // Constants, locals, globals, and calls.
......
...@@ -81,8 +81,7 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, ...@@ -81,8 +81,7 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
for (; i.has_next(); i.next()) { for (; i.has_next(); i.next()) {
WasmOpcode opcode = i.current(); WasmOpcode opcode = i.current();
if (opcode == kExprElse || opcode == kExprCatch || if (opcode == kExprElse || opcode == kExprCatch || opcode == kExprEnd) {
opcode == kExprCatchAll || opcode == kExprEnd) {
--control_depth; --control_depth;
} }
...@@ -120,8 +119,16 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, ...@@ -120,8 +119,16 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth; os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
break; break;
} }
case kExprBrOnExn: {
BranchDepthImmediate<Decoder::kNoValidate> imm_br(&i, i.pc());
ExceptionIndexImmediate<Decoder::kNoValidate> imm_idx(
&i, i.pc() + imm_br.length);
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm_br.depth << ' '
<< imm_idx.index;
break;
}
case kExprElse: case kExprElse:
case kExprCatchAll: case kExprCatch:
os << WasmOpcodes::OpcodeName(opcode); os << WasmOpcodes::OpcodeName(opcode);
control_depth++; control_depth++;
break; break;
...@@ -153,9 +160,6 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, ...@@ -153,9 +160,6 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index; os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break; break;
} }
case kExprCatch:
control_depth++;
V8_FALLTHROUGH;
case kExprThrow: { case kExprThrow: {
ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc()); ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index; os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
......
...@@ -30,22 +30,25 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -30,22 +30,25 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let except = builder.addException(kSig_v_r); let except = builder.addException(kSig_v_r);
builder.addFunction("throw_catch_null", kSig_i_i) builder.addFunction("throw_catch_null", kSig_i_i)
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmAnyRef,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Eqz, kExprI32Eqz,
kExprIf, kWasmI32, kExprIf, kWasmAnyRef,
kExprRefNull, kExprRefNull,
kExprThrow, except, kExprThrow, except,
kExprElse, kExprElse,
kExprI32Const, 42, kExprI32Const, 42,
kExprReturn,
kExprEnd, kExprEnd,
kExprCatch, except, kExprCatch,
kExprRefIsNull, kExprBrOnExn, 0, except,
kExprIf, kWasmI32, kExprRethrow,
kExprI32Const, 23, kExprEnd,
kExprElse, kExprRefIsNull,
kExprUnreachable, kExprIf, kWasmI32,
kExprEnd, kExprI32Const, 23,
kExprElse,
kExprUnreachable,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let instance = builder.instantiate(); let instance = builder.instantiate();
...@@ -83,8 +86,9 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -83,8 +86,9 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprTry, kWasmAnyRef, kExprTry, kWasmAnyRef,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprThrow, except, kExprThrow, except,
kExprCatch, except, kExprCatch,
// fall-through kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let instance = builder.instantiate(); let instance = builder.instantiate();
......
// 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
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
// Test that a catch-all block handles all exceptions thrown locally, but is
// applied only after typed catch blocks have been handled.
(function TestCatchAllLocal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v);
builder.addFunction("catchall_local", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprThrow, except1,
kExprEnd,
kExprGetLocal, 0,
kExprI32Const, 2,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprThrow, except2,
kExprEnd,
kExprI32Const, 61,
kExprCatch, except1,
kExprI32Const, 23,
kExprCatchAll,
kExprI32Const, 42,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertEquals(23, instance.exports.catchall_local(1));
assertEquals(42, instance.exports.catchall_local(2));
assertEquals(61, instance.exports.catchall_local(3));
})();
// Test that a catch-all block handles all exceptions thrown externally, even
// those originating from JavaScript instead of WebAssembly.
(function TestCatchAllExternal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_v_v);
let fun = builder.addImport("m", "f", sig_index);
let except = builder.addException(kSig_v_v);
builder.addFunction("throw", kSig_v_v)
.addBody([
kExprThrow, except
]).exportFunc();
builder.addFunction("catchall_external", kSig_i_v)
.addBody([
kExprTry, kWasmI32,
kExprCallFunction, fun,
kExprUnreachable,
kExprCatch, except,
kExprI32Const, 23,
kExprCatchAll,
kExprI32Const, 42,
kExprEnd,
]).exportFunc();
let ex_obj = new Error("my exception");
let instance = builder.instantiate({ m: { f: function() { throw ex_obj }}});
assertThrows(() => instance.exports.throw(), WebAssembly.RuntimeError);
assertEquals(42, instance.exports.catchall_external()); // From JavaScript.
try {
instance.exports.throw();
} catch (e) {
ex_obj = e;
}
assertEquals(23, instance.exports.catchall_external()); // From WebAssembly.
})();
// Test that expressions in a catch-all block are considered to be outside of
// the corresponding try block. Exceptions raised in them will percolate up.
(function TestCatchAllThrowing() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v);
builder.addFunction("catchall", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprThrow, except1,
kExprCatchAll,
kExprThrow, except2,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertThrows(() => instance.exports.catchall(), WebAssembly.RuntimeError);
})();
// Test that empty try blocks (with no expression that could potentially throw)
// are supported properly, even in the presence of unreachable catch blocks.
(function TestCatchAllEmptyBlock() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_v);
builder.addFunction("catchall", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCatch, except1,
kExprThrow, except2,
kExprCatchAll,
kExprThrow, except2,
kExprEnd
]).exportFunc();
let instance = builder.instantiate();
assertDoesNotThrow(() => instance.exports.catchall());
})();
...@@ -8,7 +8,7 @@ load("test/mjsunit/wasm/wasm-constants.js"); ...@@ -8,7 +8,7 @@ load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
load("test/mjsunit/wasm/exceptions-utils.js"); load("test/mjsunit/wasm/exceptions-utils.js");
// Test that rethrow expressions can target catch blocks. // Test that rethrow expressions work inside catch blocks.
(function TestRethrowInCatch() { (function TestRethrowInCatch() {
print(arguments.callee.name); print(arguments.callee.name);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
...@@ -17,19 +17,22 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -17,19 +17,22 @@ load("test/mjsunit/wasm/exceptions-utils.js");
.addBody([ .addBody([
kExprTry, kWasmStmt, kExprTry, kWasmStmt,
kExprThrow, except, kExprThrow, except,
kExprCatch, except, kExprCatch,
kExprRethrow, 0, kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
builder.addFunction("rethrow1", kSig_i_i) builder.addFunction("rethrow1", kSig_i_i)
.addLocals({except_count: 1})
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprThrow, except, kExprThrow, except,
kExprCatch, except, kExprCatch,
kExprSetLocal, 1,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Eqz, kExprI32Eqz,
kExprIf, kWasmStmt, kExprIf, kWasmStmt,
kExprRethrow, 1, kExprGetLocal, 1,
kExprRethrow,
kExprEnd, kExprEnd,
kExprI32Const, 23, kExprI32Const, 23,
kExprEnd kExprEnd
...@@ -41,65 +44,37 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -41,65 +44,37 @@ load("test/mjsunit/wasm/exceptions-utils.js");
assertEquals(23, instance.exports.rethrow1(1)); assertEquals(23, instance.exports.rethrow1(1));
})(); })();
// Test that rethrow expressions can target catch-all blocks. // Test that rethrow expressions work properly even in the presence of multiple
(function TestRethrowInCatchAll() { // nested handlers being involved.
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() { (function TestRethrowNested() {
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);
builder.addFunction("rethrow_nested", kSig_i_i) builder.addFunction("rethrow_nested", kSig_i_i)
.addLocals({except_count: 2})
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprThrow, except2, kExprThrow, except2,
kExprCatch, except2, kExprCatch,
kExprSetLocal, 2,
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprThrow, except1, kExprThrow, except1,
kExprCatch, except1, kExprCatch,
kExprSetLocal, 1,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 0, kExprI32Const, 0,
kExprI32Eq, kExprI32Eq,
kExprIf, kWasmStmt, kExprIf, kWasmStmt,
kExprRethrow, 1, kExprGetLocal, 1,
kExprRethrow,
kExprEnd, kExprEnd,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 1, kExprI32Const, 1,
kExprI32Eq, kExprI32Eq,
kExprIf, kWasmStmt, kExprIf, kWasmStmt,
kExprRethrow, 2, kExprGetLocal, 2,
kExprRethrow,
kExprEnd, kExprEnd,
kExprI32Const, 23, kExprI32Const, 23,
kExprEnd, kExprEnd,
...@@ -119,18 +94,22 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -119,18 +94,22 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v); let except = builder.addException(kSig_v_v);
builder.addFunction("rethrow_recatch", kSig_i_i) builder.addFunction("rethrow_recatch", kSig_i_i)
.addLocals({except_count: 1})
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprThrow, except, kExprThrow, except,
kExprCatch, except, kExprCatch,
kExprSetLocal, 1,
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Eqz, kExprI32Eqz,
kExprIf, kWasmStmt, kExprIf, kWasmStmt,
kExprRethrow, 2, kExprGetLocal, 1,
kExprRethrow,
kExprEnd, kExprEnd,
kExprI32Const, 42, kExprI32Const, 42,
kExprCatch, except, kExprCatch,
kExprDrop,
kExprI32Const, 23, kExprI32Const, 23,
kExprEnd, kExprEnd,
kExprEnd, kExprEnd,
......
...@@ -33,7 +33,9 @@ function NewExportedException() { ...@@ -33,7 +33,9 @@ function NewExportedException() {
.addBody([ .addBody([
kExprTry, kWasmStmt, kExprTry, kWasmStmt,
kExprCallFunction, fun, kExprCallFunction, fun,
kExprCatch, except, kExprCatch,
kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let ex_obj = new Error("my exception"); let ex_obj = new Error("my exception");
...@@ -65,7 +67,9 @@ function NewExportedException() { ...@@ -65,7 +67,9 @@ function NewExportedException() {
.addBody([ .addBody([
kExprTry, kWasmStmt, kExprTry, kWasmStmt,
kExprCallFunction, fun, kExprCallFunction, fun,
kExprCatch, except, kExprCatch,
kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let ex_obj = new Error("my exception"); let ex_obj = new Error("my exception");
...@@ -100,7 +104,9 @@ function NewExportedException() { ...@@ -100,7 +104,9 @@ function NewExportedException() {
.addBody([ .addBody([
kExprTry, kWasmStmt, kExprTry, kWasmStmt,
kExprCallFunction, fun, kExprCallFunction, fun,
kExprCatch, except1, kExprCatch,
kExprBrOnExn, 0, except1,
kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let ex_obj = new Error("my exception"); let ex_obj = new Error("my exception");
...@@ -139,7 +145,9 @@ function NewExportedException() { ...@@ -139,7 +145,9 @@ function NewExportedException() {
.addBody([ .addBody([
kExprTry, kWasmStmt, kExprTry, kWasmStmt,
kExprCallFunction, fun, kExprCallFunction, fun,
kExprCatch, except, kExprCatch,
kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd, kExprEnd,
]).exportFunc(); ]).exportFunc();
let ex_obj = new Error("my exception"); let ex_obj = new Error("my exception");
......
...@@ -32,15 +32,17 @@ load("test/mjsunit/wasm/exceptions-utils.js"); ...@@ -32,15 +32,17 @@ load("test/mjsunit/wasm/exceptions-utils.js");
builder.addFunction("throw_catch_simd", kSig_i_v) builder.addFunction("throw_catch_simd", kSig_i_v)
.addLocals({s128_count: 1}) .addLocals({s128_count: 1})
.addBody([ .addBody([
kExprTry, kWasmI32, kExprTry, kWasmS128,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprThrow, 0, kExprThrow, 0,
kExprCatch, except, kExprCatch,
kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd,
// TODO(mstarzinger): Actually return some compressed form of the s128 // TODO(mstarzinger): Actually return some compressed form of the s128
// value here to make sure it is extracted properly from the exception. // value here to make sure it is extracted properly from the exception.
kExprDrop, kExprDrop,
kExprI32Const, 1, kExprI32Const, 1,
kExprEnd,
]) ])
.exportFunc(); .exportFunc();
var instance = builder.instantiate(); var instance = builder.instantiate();
......
This diff is collapsed.
...@@ -185,7 +185,7 @@ let kExprTry = 0x06; ...@@ -185,7 +185,7 @@ let kExprTry = 0x06;
let kExprCatch = 0x07; let kExprCatch = 0x07;
let kExprThrow = 0x08; let kExprThrow = 0x08;
let kExprRethrow = 0x09; let kExprRethrow = 0x09;
let kExprCatchAll = 0x0a; let kExprBrOnExn = 0x0a;
let kExprEnd = 0x0b; let kExprEnd = 0x0b;
let kExprBr = 0x0c; let kExprBr = 0x0c;
let kExprBrIf = 0x0d; let kExprBrIf = 0x0d;
......
...@@ -2445,58 +2445,57 @@ TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) { ...@@ -2445,58 +2445,57 @@ TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) {
} }
#define WASM_TRY_OP kExprTry, kLocalVoid #define WASM_TRY_OP kExprTry, kLocalVoid
#define WASM_CATCH(index) kExprCatch, static_cast<byte>(index) #define WASM_BR_ON_EXN(depth, index) \
#define WASM_RETHROW(depth) kExprRethrow, static_cast<byte>(depth) kExprBrOnExn, static_cast<byte>(depth), static_cast<byte>(index)
TEST_F(FunctionBodyDecoderTest, TryCatch) { TEST_F(FunctionBodyDecoderTest, TryCatch) {
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte ex1 = builder.AddException(sigs.v_v()); EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatch, kExprDrop, kExprEnd);
byte ex2 = builder.AddException(sigs.v_v()); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatch, kExprCatch, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprEnd); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); // Missing catch.
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_CATCH(ex2), kExprEnd); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatch); // Missing end.
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); // Missing catch. EXPECT_FAILURE(v_v, kExprCatch, kExprEnd); // Missing try.
EXPECT_FAILURE(v_v, WASM_TRY_OP, WASM_CATCH(ex1)); // Missing end.
EXPECT_FAILURE(v_v, WASM_CATCH(ex1), kExprEnd); // Missing try.
} }
TEST_F(FunctionBodyDecoderTest, TryCatchAll) { TEST_F(FunctionBodyDecoderTest, Rethrow) {
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte ex1 = builder.AddException(sigs.v_v()); EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatch, kExprRethrow, kExprEnd);
byte ex2 = builder.AddException(sigs.v_v()); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprRethrow, kExprCatch, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, kExprEnd); EXPECT_FAILURE(v_v, WASM_BLOCK(kExprRethrow));
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), kExprCatchAll, kExprEnd); EXPECT_FAILURE(v_v, kExprRethrow);
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.
EXPECT_FAILURE(v_v, kExprCatchAll, kExprEnd); // Missing try.
} }
TEST_F(FunctionBodyDecoderTest, Rethrow) { TEST_F(FunctionBodyDecoderTest, BrOnExn) {
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte ex1 = builder.AddException(sigs.v_v()); byte ex1 = builder.AddException(sigs.v_v());
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(ex1), WASM_RETHROW(0), kExprEnd); byte ex2 = builder.AddException(sigs.v_i());
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, WASM_RETHROW(0), kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(0, ex1),
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatchAll, WASM_BLOCK(WASM_RETHROW(1)), kExprDrop, kExprEnd);
kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(1, ex1),
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatchAll, WASM_BLOCK(WASM_RETHROW(0)), kExprDrop, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(0, ex1),
WASM_BR_ON_EXN(0, ex1), kExprDrop, kExprEnd);
EXPECT_VERIFIES(v_v, WASM_BLOCK(WASM_TRY_OP, kExprCatch,
WASM_BR_ON_EXN(1, ex1), kExprDrop, kExprEnd));
EXPECT_VERIFIES(i_v,
WASM_BLOCK_I(WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(1, ex2),
kExprDrop, kExprEnd, kExprI32Const, 0));
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(2, ex1),
kExprDrop, kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatch, kExprDrop,
WASM_BR_ON_EXN(0, ex1), kExprEnd);
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprCatch, WASM_BR_ON_EXN(0, ex1),
kExprEnd); 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_BR_ON_EXN
#undef WASM_TRY_OP #undef WASM_TRY_OP
#undef WASM_CATCH
#undef WASM_RETHROW
TEST_F(FunctionBodyDecoderTest, MultiValBlock1) { TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
WASM_FEATURE_SCOPE(mv); WASM_FEATURE_SCOPE(mv);
...@@ -2951,11 +2950,12 @@ TEST_F(WasmOpcodeLengthTest, Statements) { ...@@ -2951,11 +2950,12 @@ TEST_F(WasmOpcodeLengthTest, Statements) {
EXPECT_LENGTH(1, kExprElse); EXPECT_LENGTH(1, kExprElse);
EXPECT_LENGTH(1, kExprEnd); EXPECT_LENGTH(1, kExprEnd);
EXPECT_LENGTH(1, kExprSelect); EXPECT_LENGTH(1, kExprSelect);
EXPECT_LENGTH(1, kExprCatch);
EXPECT_LENGTH(1, kExprRethrow);
EXPECT_LENGTH(2, kExprBr); EXPECT_LENGTH(2, kExprBr);
EXPECT_LENGTH(2, kExprBrIf); EXPECT_LENGTH(2, kExprBrIf);
EXPECT_LENGTH(2, kExprThrow); EXPECT_LENGTH(2, kExprThrow);
EXPECT_LENGTH(2, kExprRethrow); EXPECT_LENGTH(3, kExprBrOnExn);
EXPECT_LENGTH(2, kExprCatch);
EXPECT_LENGTH_N(2, kExprBlock, kLocalI32); EXPECT_LENGTH_N(2, kExprBlock, kLocalI32);
EXPECT_LENGTH_N(2, kExprLoop, kLocalI32); EXPECT_LENGTH_N(2, kExprLoop, kLocalI32);
EXPECT_LENGTH_N(2, kExprIf, kLocalI32); EXPECT_LENGTH_N(2, kExprIf, kLocalI32);
......
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