diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManger.java b/src/de/inetsoftware/jwebassembly/module/BranchManger.java index 3e820d6..cfbb3b3 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManger.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManger.java @@ -62,6 +62,9 @@ class BranchManger { private TryCatchFinally[] exceptionTable; + private final ArrayList breakOperations = new ArrayList<>(); + + /** * Create a branch manager. * @@ -88,7 +91,7 @@ class BranchManger { allParsedOperations.clear(); root.clear(); loops.clear(); - root.endPos = code.getCodeSize(); + root.endPos = root.elseEndPos = code.getCodeSize(); exceptionTable = code.getExceptionTable(); for( TryCatchFinally ex : exceptionTable ) { if ( ex.getStart() == ex.getHandler() ) { @@ -161,6 +164,9 @@ class BranchManger { List parsedOperations = allParsedOperations; Collections.sort( parsedOperations ); calculate( root, parsedOperations ); + for( BreakBlock breakBlock : breakOperations ) { + calculateBreak( breakBlock ); + } } /** @@ -393,6 +399,7 @@ class BranchManger { branch.add( new BranchNode( positions.elsePos, endPos, WasmBlockOperator.BR, null, breakDeep + 1 ) ); endPos = branch.endPos; } else { + branch.elseEndPos = endPos; branch = new BranchNode( positions.elsePos, endPos, WasmBlockOperator.ELSE, WasmBlockOperator.END ); parent.add( branch ); } @@ -406,6 +413,29 @@ class BranchManger { if( branch == null ) { branch = new BranchNode( startPos, endPos, WasmBlockOperator.IF, WasmBlockOperator.END, ValueType.empty ); parent.add( branch ); + if( startBlock.endPosition > parent.endPos ) { + int count = 0; + for( int j = 0; j < instructions.size(); j++ ) { + WasmInstruction instr = instructions.get( j ); + int pos = instr.getCodePosition(); + if( pos < startPos ) { + continue; + } + if( pos >= endPos ) { + break; + } + if( instr.getType() == Type.Jump ) { + continue; + } + count++; + } + if( count == 0 ) { + // we jump outside the parent and there are no instructions. This is lie a conditional break + //TODO should be BR_IF + breakOperations.add( new BreakBlock( branch, startBlock.endPosition ) ); + return; + } + } } startBlock.negateCompare(); @@ -585,7 +615,7 @@ class BranchManger { private int calculateBreakDeep( BranchNode parent, int endPos ) { int deep = -1; boolean wasLoop = false; - while( parent != null && parent.endPos == endPos && parent.data == null ) { + while( parent != null && parent.elseEndPos <= endPos && parent.data == null ) { deep++; wasLoop |= parent.startOp == WasmBlockOperator.LOOP; // only in a loop we need to jump for exit parent = parent.parent; @@ -1160,6 +1190,43 @@ class BranchManger { return null; } + /** + * Add the break to the block hierarchy. Add an extra block if needed and correct the break deep of other + * instructions. + * + * @param breakBlock + * the description of the break. + */ + private void calculateBreak( BreakBlock breakBlock ) { + int deep = -1; + int gotoEndPos = breakBlock.endPosition; + BranchNode branch = breakBlock.branch; + BranchNode parent = branch; + while( parent != null && parent.elseEndPos < gotoEndPos ) { + deep++; + parent = parent.parent; + } + + if( parent != null && parent.elseEndPos > gotoEndPos ) { + BranchNode middleNode = new BranchNode( parent.startPos, gotoEndPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); + for( Iterator it = parent.iterator(); it.hasNext(); ) { + BranchNode child = it.next(); + if( child.endPos > gotoEndPos ) { + continue; + } + middleNode.add( child ); + it.remove(); + } + parent.add( middleNode ); + //deep++; + parent = middleNode; + patchBrDeep( middleNode ); + } + + BranchNode breakNode = new BranchNode( branch.endPos, branch.endPos, WasmBlockOperator.BR, null, deep + 1 ); + branch.add( breakNode ); + } + /** * Check on every instruction position if there any branch is ending * @@ -1349,6 +1416,8 @@ class BranchManger { */ private int startIdx; + private int elseEndPos; + /** * Create a new description. * @@ -1381,7 +1450,7 @@ class BranchManger { */ BranchNode( int startPos, int endPos, WasmBlockOperator startOp, WasmBlockOperator endOp, Object data ) { this.startPos = startPos; - this.endPos = endPos; + this.endPos = this.elseEndPos = endPos; this.startOp = startOp; this.endOp = endOp; this.data = data; @@ -1539,4 +1608,27 @@ class BranchManger { /** The position of the first instruction in the ELSE part. */ private int elsePos; } + + /** + * Described a break to a block that will be added later. + */ + private static class BreakBlock { + + private final int endPosition; + + private final BranchNode branch; + + /** + * Create Break + * + * @param branch + * the parent block which should contain the break + * @param endPosition + * the Jump position + */ + public BreakBlock( BranchNode branch, int endPosition ) { + this.endPosition = endPosition; + this.branch = branch; + } + } } diff --git a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java index 215b19c..7054cf8 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java +++ b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java @@ -74,6 +74,10 @@ public class ControlFlowOperators extends AbstractBaseTest { addParam( list, script, "ifOrWithMulti" ); addParam( list, script, "ifMultipleInsideThen" ); addParam( list, script, "ifWithConditionalInsideThen" ); + addParam( list, script, "conditionalInsideIf_1" ); + addParam( list, script, "conditionalInsideIf_2" ); + addParam( list, script, "conditionalInsideIf_3" ); + addParam( list, script, "conditionalInsideIf_4" ); addParam( list, script, "stringSwitchNormalFoo" ); addParam( list, script, "stringSwitchNormalBar" ); addParam( list, script, "stringSwitchNormalDefault" ); @@ -547,7 +551,7 @@ public class ControlFlowOperators extends AbstractBaseTest { } @Export - static int ifWithConditionalInsideThen() throws CloneNotSupportedException { + static int ifWithConditionalInsideThen() { int val = 42; int result = 0; if( val > 20 ) { @@ -560,6 +564,34 @@ public class ControlFlowOperators extends AbstractBaseTest { return result + 13; } + @Export + static int conditionalInsideIf_1() { + return conditionalInsideIf( null, null, null ); + } + + @Export + static int conditionalInsideIf_2() { + return conditionalInsideIf( null, null, "foo" ); + } + + @Export + static int conditionalInsideIf_3() { + return conditionalInsideIf( "foo", null, null ); + } + + @Export + static int conditionalInsideIf_4() { + return conditionalInsideIf( null, "foo", null ); + } + + static int conditionalInsideIf( Object a, Object b, Object c ) { + if( (a == null ? b == null : a == b ) ) { + return c == null ? 1 : 2; + } else { + return 3; + } + } + @Export static int stringSwitchNormalFoo() { return stringSwitchNormal( "foo" );