Skip to content

Commit bced89d

Browse files
authored
Fix inner block problem with 'catch' (#3129)
Fixes #3114.
1 parent bcc6f29 commit bced89d

File tree

4 files changed

+104
-21
lines changed

4 files changed

+104
-21
lines changed

src/wasm-binary.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,7 @@ class WasmBinaryBuilder {
13941394
void visitBlock(Block* curr);
13951395

13961396
// Gets a block of expressions. If it's just one, return that singleton.
1397-
Expression* getBlockOrSingleton(Type type, unsigned numPops = 0);
1397+
Expression* getBlockOrSingleton(Type type);
13981398

13991399
void visitIf(If* curr);
14001400
void visitLoop(Loop* curr);
@@ -1444,7 +1444,7 @@ class WasmBinaryBuilder {
14441444
void visitRefNull(RefNull* curr);
14451445
void visitRefIsNull(RefIsNull* curr);
14461446
void visitRefFunc(RefFunc* curr);
1447-
void visitTry(Try* curr);
1447+
void visitTryOrTryInBlock(Expression*& out);
14481448
void visitThrow(Throw* curr);
14491449
void visitRethrow(Rethrow* curr);
14501450
void visitBrOnExn(BrOnExn* curr);

src/wasm/wasm-binary.cpp

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,7 +2441,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
24412441
visitRefFunc((curr = allocator.alloc<RefFunc>())->cast<RefFunc>());
24422442
break;
24432443
case BinaryConsts::Try:
2444-
visitTry((curr = allocator.alloc<Try>())->cast<Try>());
2444+
visitTryOrTryInBlock(curr);
24452445
break;
24462446
case BinaryConsts::Throw:
24472447
visitThrow((curr = allocator.alloc<Throw>())->cast<Throw>());
@@ -2692,21 +2692,11 @@ void WasmBinaryBuilder::visitBlock(Block* curr) {
26922692
}
26932693

26942694
// Gets a block of expressions. If it's just one, return that singleton.
2695-
// numPops is the number of pop instructions we add before starting to parse the
2696-
// block. Can be used when we need to assume certain number of values are on top
2697-
// of the stack in the beginning.
2698-
Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type,
2699-
unsigned numPops) {
2695+
Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type) {
27002696
Name label = getNextLabel();
27012697
breakStack.push_back({label, type});
27022698
auto start = expressionStack.size();
27032699

2704-
Builder builder(wasm);
2705-
for (unsigned i = 0; i < numPops; i++) {
2706-
auto* pop = builder.makePop(Type::exnref);
2707-
pushExpression(pop);
2708-
}
2709-
27102700
processExpressions();
27112701
size_t end = expressionStack.size();
27122702
if (end < start) {
@@ -2757,12 +2747,12 @@ void WasmBinaryBuilder::visitLoop(Loop* curr) {
27572747
auto start = expressionStack.size();
27582748
processExpressions();
27592749
size_t end = expressionStack.size();
2750+
if (start > end) {
2751+
throwError("block cannot pop from outside");
2752+
}
27602753
if (end - start == 1) {
27612754
curr->body = popExpression();
27622755
} else {
2763-
if (start > end) {
2764-
throwError("block cannot pop from outside");
2765-
}
27662756
auto* block = allocator.alloc<Block>();
27672757
pushBlockElements(block, curr->type, start);
27682758
block->finalize(curr->type);
@@ -4834,8 +4824,9 @@ void WasmBinaryBuilder::visitRefFunc(RefFunc* curr) {
48344824
curr->finalize();
48354825
}
48364826

4837-
void WasmBinaryBuilder::visitTry(Try* curr) {
4827+
void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) {
48384828
BYN_TRACE("zz node: Try\n");
4829+
auto* curr = allocator.alloc<Try>();
48394830
startControlFlow(curr);
48404831
// For simplicity of implementation, like if scopes, we create a hidden block
48414832
// within each try-body and catch-body, and let branches target those inner
@@ -4845,11 +4836,84 @@ void WasmBinaryBuilder::visitTry(Try* curr) {
48454836
if (lastSeparator != BinaryConsts::Catch) {
48464837
throwError("No catch instruction within a try scope");
48474838
}
4848-
curr->catchBody = getBlockOrSingleton(curr->type, 1);
4839+
4840+
// For simplicity, we create an inner block within the catch body too, but the
4841+
// one within the 'catch' *must* be omitted when we write out the binary back
4842+
// later, because the 'catch' instruction pushes a value onto the stack and
4843+
// the inner block does not support block input parameters without multivalue
4844+
// support.
4845+
// try
4846+
// ...
4847+
// catch ;; Pushes a value onto the stack
4848+
// block ;; Inner block. Should be deleted when writing binary!
4849+
// use the pushed value
4850+
// end
4851+
// end
4852+
//
4853+
// But when input binary code is like
4854+
// try
4855+
// ...
4856+
// catch
4857+
// br 0
4858+
// end
4859+
//
4860+
// 'br 0' accidentally happens to target the inner block, creating code like
4861+
// this in Binaryen IR, making the inner block not deletable, resulting in a
4862+
// validation error:
4863+
// (try
4864+
// ...
4865+
// (catch
4866+
// (block $label0 ;; Cannot be deleted, because there's a branch to this
4867+
// ...
4868+
// (br $label0)
4869+
// )
4870+
// )
4871+
// )
4872+
//
4873+
// When this happens, we fix this by creating a block that wraps the whole
4874+
// try-catch, and making the branches target that block instead, like this:
4875+
// (block $label ;; New enclosing block, new target for the branch
4876+
// (try
4877+
// ...
4878+
// (catch
4879+
// (block ;; Now this can be deleted when writing binary
4880+
// ...
4881+
// (br $label0)
4882+
// )
4883+
// )
4884+
// )
4885+
// )
4886+
Name catchLabel = getNextLabel();
4887+
breakStack.push_back({catchLabel, curr->type});
4888+
auto start = expressionStack.size();
4889+
4890+
Builder builder(wasm);
4891+
pushExpression(builder.makePop(Type::exnref));
4892+
4893+
processExpressions();
4894+
size_t end = expressionStack.size();
4895+
if (start > end) {
4896+
throwError("block cannot pop from outside");
4897+
}
4898+
if (end - start == 1) {
4899+
curr->catchBody = popExpression();
4900+
} else {
4901+
auto* block = allocator.alloc<Block>();
4902+
pushBlockElements(block, curr->type, start);
4903+
block->finalize(curr->type);
4904+
curr->catchBody = block;
4905+
}
48494906
curr->finalize(curr->type);
4850-
if (lastSeparator != BinaryConsts::End) {
4851-
throwError("try should end with end");
4907+
4908+
if (breakTargetNames.find(catchLabel) == breakTargetNames.end()) {
4909+
out = curr;
4910+
} else {
4911+
// Create a new block that encloses the whole try-catch
4912+
auto* block = builder.makeBlock(catchLabel, curr);
4913+
out = block;
48524914
}
4915+
breakStack.pop_back();
4916+
breakTargetNames.erase(catchLabel);
48534917
}
48544918

48554919
void WasmBinaryBuilder::visitThrow(Throw* curr) {

test/break-within-catch.wasm

32 Bytes
Binary file not shown.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(func $0
4+
(block $label$2
5+
(try
6+
(do
7+
(nop)
8+
)
9+
(catch
10+
(drop
11+
(pop exnref)
12+
)
13+
(br $label$2)
14+
)
15+
)
16+
)
17+
)
18+
)
19+

0 commit comments

Comments
 (0)