diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManger.java b/src/de/inetsoftware/jwebassembly/module/BranchManger.java index 6eed244..7e3e104 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManger.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManger.java @@ -18,6 +18,7 @@ package de.inetsoftware.jwebassembly.module; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import de.inetsoftware.classparser.CodeInputStream; @@ -29,13 +30,16 @@ import de.inetsoftware.classparser.CodeInputStream; */ class BranchManger { - private final ArrayList stack = new ArrayList<>(); + private final ArrayList allParsedOperations = new ArrayList<>(); + + private final BranchNode root = new BranchNode( 0, Integer.MAX_VALUE, null, null ); /** - * Remove all branch information. + * Remove all branch information for reusing the manager. */ void reset() { - stack.clear(); + allParsedOperations.clear(); + root.clear(); } /** @@ -49,19 +53,31 @@ class BranchManger { * the relative jump position */ void start( BlockOperator op, int startPosition, int offset ) { - stack.add( new Block( op, startPosition, offset ) ); + allParsedOperations.add( new ParsedBlock( op, startPosition, offset ) ); } /** * Calculate all block operators from the parsed information. */ void calculate() { - for( int i = 0; i < stack.size(); i++ ) { - Block block = stack.get( i ); - switch( block.op ) { + calculate( root, allParsedOperations ); + } + + /** + * Calculate the branch tree for the given branch and parsed sub operations. + * + * @param parent the parent branch/block in the hierarchy. + * @param parsedOperations the not consumed parsed operations of the parent block. + */ + private void calculate( BranchNode parent, List parsedOperations ) { + while( !parsedOperations.isEmpty() ) { + ParsedBlock parsedBlock = parsedOperations.remove( 0 ); + switch( parsedBlock.op ) { case IF: - caculateIf( i, block ); + caculateIf( parent, parsedBlock, parsedOperations ); break; + default: + throw new IllegalStateException( "Unimplemented block code operation: " + parsedBlock.op ); } } } @@ -69,39 +85,54 @@ class BranchManger { /** * Calculate the ELSE and END position of an IF control structure. * - * @param i - * the index in the stack + * @param parent + * the parent branch * @param startBlock * the start block of the if control structure + * @param parsedOperations + * the not consumed operations in the parent branch */ - private void caculateIf( int i, Block startBlock ) { - i++; - int gotoPos = startBlock.endPosition - 3; // 3 - byte size of goto instruction - for( ; i < stack.size(); i++ ) { - Block block = stack.get( i ); - if( block.startPosition == gotoPos && block.op == BlockOperator.GOTO ) { - block.op = BlockOperator.ELSE; - block.startPosition += 3; - startBlock = block; - i++; + private void caculateIf( BranchNode parent, ParsedBlock startBlock, List parsedOperations ) { + int i = 0; + int endPos = Math.min( startBlock.endPosition, parent.endPos ); + int gotoPos = endPos - 3; // 3 - byte size of goto instruction + BranchNode branch = null; + for( ; i < parsedOperations.size(); i++ ) { + ParsedBlock parsedBlock = parsedOperations.get( i ); + if( parsedBlock.startPosition == gotoPos && parsedBlock.op == BlockOperator.GOTO ) { + parsedOperations.remove( i ); + branch = new BranchNode( startBlock.startPosition, startBlock.endPosition, BlockOperator.IF, null ); + parent.add( branch ); + if( i > 0 ) { + calculate( branch, parsedOperations.subList( 0, i ) ); + } + endPos = parsedBlock.endPosition; + branch = new BranchNode( startBlock.endPosition, endPos, BlockOperator.ELSE, BlockOperator.END ); + parent.add( branch ); break; } - if( block.startPosition > gotoPos ) { + if( parsedBlock.startPosition > gotoPos ) { break; } } + if( branch == null ) { + branch = new BranchNode( startBlock.startPosition, endPos, BlockOperator.IF, BlockOperator.END ); + parent.add( branch ); + } + /** * Search the index in the stack to add the END operator */ - int endPos = startBlock.endPosition; - for( ; i < stack.size(); i++ ) { - Block block = stack.get( i ); - if( block.startPosition >= endPos ) { + for( ; i < parsedOperations.size(); i++ ) { + ParsedBlock parsedBlock = parsedOperations.get( i ); + if( parsedBlock.startPosition >= endPos ) { break; } } - stack.add( i, new Block( BlockOperator.END, endPos, 0 ) ); + if( i > 0 ) { + calculate( branch, parsedOperations.subList( 0, i ) ); + } } /** @@ -115,31 +146,71 @@ class BranchManger { * if any I/O exception occur */ void handle( CodeInputStream byteCode, ModuleWriter writer ) throws IOException { - if( stack.isEmpty() ) { - return; - } - int position = byteCode.getCodePosition(); - Block block = stack.get( 0 ); - if( block.startPosition == position ) { - writer.writeBlockCode( block.op ); - stack.remove( 0 ); - } + root.handle( byteCode.getCodePosition(), writer ); } /** - * Description of single block/branch + * Description of single block/branch from the parsed Java byte code. The parsed branches are plain. */ - private static class Block { + private static class ParsedBlock { private BlockOperator op; private int startPosition; private int endPosition; - private Block( BlockOperator op, int startPosition, int offset ) { + private ParsedBlock( BlockOperator op, int startPosition, int offset ) { this.op = op; this.startPosition = startPosition; this.endPosition = startPosition + offset; } } + + /** + * Described a code branch/block node in a tree structure. + */ + private static class BranchNode extends ArrayList { + + final int startPos; + + final int endPos; + + private final BlockOperator startOp; + + private final BlockOperator endOp; + + /** + * Create a new description. + * + * @param startPos + * the start position in the Java code. Limit also the children. + * @param endPos + * the end position in the Java code. Limit also the children. + * @param startOp + * The WASM operation on the start position. Can be null if there is nothing in WASM. + * @param endOp + * the WASM operation on the end position. Can be null if there is nothing in WASM. + */ + BranchNode( int startPos, int endPos, BlockOperator startOp, BlockOperator endOp ) { + this.startPos = startPos; + this.endPos = endPos; + this.startOp = startOp; + this.endOp = endOp; + } + + void handle( int codePositions, ModuleWriter writer ) throws IOException { + if( codePositions < startPos || codePositions > endPos ) { + return; + } + if( codePositions == startPos && startOp != null ) { + writer.writeBlockCode( startOp ); + } + for( BranchNode branch : this ) { + branch.handle( codePositions, writer ); + } + if( codePositions == endPos && endOp != null ) { + writer.writeBlockCode( endOp ); + } + } + } } diff --git a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java index b7d601a..a198bfb 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java +++ b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java @@ -42,6 +42,7 @@ public class ControlFlowOperators extends AbstractBaseTest { addParam( list, script, "ifeq" ); addParam( list, script, "ifne" ); addParam( list, script, "iflt" ); + addParam( list, script, "ifMultiple" ); addParam( list, script, "forLoop" ); } return list; @@ -80,6 +81,23 @@ public class ControlFlowOperators extends AbstractBaseTest { return condition; } + @Export + static int ifMultiple() { + int condition = 3; + if( condition <= 0 ) { + if( condition < 0 ) { + condition = 13; + } + } else { + if( condition > 0 ) { + condition++; + } else { + condition--; + } + } + return condition; + } + @Export static int forLoop() { int a = 0;