diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManger.java b/src/de/inetsoftware/jwebassembly/module/BranchManger.java index ad37433..d3982c0 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManger.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManger.java @@ -183,6 +183,7 @@ class BranchManger { loop = new ParsedBlock( JavaBlockOperator.LOOP, start, 0, start, parsedBlock.lineNumber ); loops.put( start, loop ); } + loop.nextPosition = parsedBlock.startPosition; // Jump position for Continue loop.endPosition = parsedBlock.nextPosition; break; default: @@ -262,7 +263,7 @@ class BranchManger { for( i = 0; i < instructions.size(); i++ ) { WasmInstruction instr = instructions.get( i ); int codePos = instr.getCodePosition(); - if( codePos == nextPos ) { + if( codePos == nextPos && conditionIdx < 0 ) { conditionIdx = i; } if( codePos >= conditionEnd ) { @@ -276,6 +277,7 @@ class BranchManger { } gotoBlock.op = JavaBlockOperator.LOOP; + gotoBlock.nextPosition = conditionStart; // Jump position for Continue gotoBlock.endPosition = conditionEnd; instructions.add( i, new WasmBlockInstruction( WasmBlockOperator.BR, 0, conditionNew, gotoBlock.lineNumber ) ); instructions.add( conditionIdx++, new WasmBlockInstruction( WasmBlockOperator.BR_IF, 1, conditionNew, gotoBlock.lineNumber ) ); @@ -312,6 +314,33 @@ class BranchManger { } } + /** + * Add a break to the node if the block jump to the continue position of an outer loop. + * + * @param parent + * the container for adding the break + * @param startBlock + * an IF or GOTO block. + * @return true, if the break was added + */ + private boolean addBreakIfLoopContinue( BranchNode parent, ParsedBlock startBlock ) { + BranchNode main = parent; + int endPos = startBlock.endPosition; + int breakDeep = 0; + while( main != null ) { + if( main.startOp == WasmBlockOperator.LOOP && main.continuePos == endPos ) { + // possible operation values here are IF and GOTO + WasmBlockOperator op = startBlock.op == JavaBlockOperator.IF ? WasmBlockOperator.BR_IF : WasmBlockOperator.BR; + int startPos = startBlock.nextPosition; + parent.add( new BranchNode( startPos, startPos, op, null, breakDeep ) ); + return true; + } + main = main.parent; + breakDeep++; + } + return false; + } + /** * Calculate the ELSE and END position of an IF control structure. * @@ -326,6 +355,10 @@ class BranchManger { instructions.remove( startBlock.jump ); IfPositions positions = searchElsePosition( startBlock, parsedOperations ); + if( addBreakIfLoopContinue( parent, startBlock ) ) { + return; + } + BranchNode main = parent; for( int i = 0; i < positions.ifCount; i++ ) { IfParsedBlock parsedBlock = (IfParsedBlock)parsedOperations.remove( 0 ); @@ -389,6 +422,11 @@ class BranchManger { i = 0; } + if( addBreakIfLoopContinue( branch, parsedBlock ) ) { + branch.endOp = WasmBlockOperator.END; + endPos = branch.endPos; + break; + } // if with block type signature must have an else block int breakDeep = calculateBreakDeep( parent, endPos ); if( breakDeep > 0 ) { @@ -863,6 +901,29 @@ class BranchManger { blockInstr.setData( data + 1 ); } } + patchBrDeepInTree( newNode, 0 ); + } + + /** + * Patch the existing BR instructions in the tree after a new BLOCK node was injected in the hierarchy. The break position must + * only increment if the old break position is outside of the new BLOCK. + * + * @param newNode + * the new node + * @param deep + * the deep of recursion + */ + private void patchBrDeepInTree( BranchNode newNode, int deep ) { + for( BranchNode node : newNode ) { + if( node.startOp == WasmBlockOperator.BR || node.startOp == WasmBlockOperator.BR_IF ) { + Integer data = (Integer)node.data; + if( data >= deep ) { + node.data = data + 1; + } + } else { + patchBrDeepInTree( node, deep + 1 ); + } + } } /** @@ -906,6 +967,7 @@ class BranchManger { return; } } + //breakOperations.add( new BreakBlock( parent, jump ) ); throw new WasmException( "GOTO code without target loop/block. Jump from " + start + " to " + jump, gotoBlock.lineNumber ); } @@ -922,6 +984,7 @@ class BranchManger { BranchNode blockNode = new BranchNode( loopBlock.startPosition, loopBlock.endPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); parent.add( blockNode ); BranchNode loopNode = new BranchNode( loopBlock.startPosition, loopBlock.endPosition, WasmBlockOperator.LOOP, WasmBlockOperator.END ); + loopNode.continuePos = loopBlock.nextPosition; blockNode.add( loopNode ); int idx = 0; @@ -1415,6 +1478,9 @@ class BranchManger { private int elseEndPos; + /** jump position for a CONTINUE in a loop */ + private int continuePos; + /** * Create a new description. * diff --git a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java index 7054cf8..cca9042 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java +++ b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java @@ -50,6 +50,7 @@ public class ControlFlowOperators extends AbstractBaseTest { addParam( list, script, "doWhileLoopTwoConditions" ); addParam( list, script, "doWhileLoopWithBreak" ); addParam( list, script, "whileLoop" ); + addParam( list, script, "whileLoopWithContinue" ); addParam( list, script, "forLoop" ); addParam( list, script, "conditionalOperator" ); addParam( list, script, "conditionalOperator2" ); @@ -334,6 +335,20 @@ public class ControlFlowOperators extends AbstractBaseTest { return b; } + @Export + public static int whileLoopWithContinue() { + int i = 0; + int value = 0; + while( i < 16 ) { + i++; + if( i > 8 ) { + continue; + } + value |= 1 << i; + } + return value; + } + @Export static int forLoop() { int a = 0;