@@ -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
48554919void WasmBinaryBuilder::visitThrow (Throw* curr) {
0 commit comments