diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManager.java b/src/de/inetsoftware/jwebassembly/module/BranchManager.java index ba86ad8..0ccf300 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManager.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManager.java @@ -203,9 +203,18 @@ class BranchManager { // if a condition form outside of the loop point inside the loop then it must be conditional return and a jump to the loop condition. for( int n = b - 1; n >= 0; n-- ) { ParsedBlock prevBlock = parsedOperations.get( n ); - if( prevBlock.op == JavaBlockOperator.IF && prevBlock.startPosition < start && prevBlock.endPosition > start - && prevBlock.endPosition < parsedBlock.startPosition ) { - prevBlock.endPosition = start; + switch( prevBlock.op ) { + case IF: + if( prevBlock.startPosition < start && prevBlock.endPosition > start && prevBlock.endPosition < parsedBlock.startPosition ) { + prevBlock.endPosition = start; + } + break; + case LOOP: + if( start == prevBlock.nextPosition ) { + loop.startPosition = prevBlock.startPosition; + } + break; + default: } } } @@ -543,31 +552,16 @@ class BranchManager { } if( branch == null ) { + if( startBlock.endPosition > parent.endPos ) { + // we jump outside the parent. This is like a conditional break. + //TODO should be BR_IF + branch = new BranchNode( startPos - 1, startPos, WasmBlockOperator.IF, WasmBlockOperator.END, ValueType.empty ); + parent.add( branch ); + breakOperations.add( new BreakBlock( branch, startPos, startBlock.endPosition ) ); + return; + } branch = new BranchNode( startPos - 1, 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 like a conditional break - //TODO should be BR_IF - breakOperations.add( new BreakBlock( branch, endPos, startBlock.endPosition ) ); - return; - } - } } startBlock.negateCompare(); @@ -1062,21 +1056,22 @@ class BranchManager { * @param parsedOperations * the not consumed operations in the parent branch */ - private void calculateGoto ( BranchNode parent, ParsedBlock gotoBlock, List parsedOperations ) { + private void calculateGoto ( @Nonnull BranchNode parent, @Nonnull ParsedBlock gotoBlock, List parsedOperations ) { int jump = gotoBlock.endPosition; // jump position of the GOTO int start = gotoBlock.startPosition; if( start > jump ) { // back jump to a previous position like in loops int deep = 0; - while( parent != null ) { - if( parent.startOp == WasmBlockOperator.LOOP && parent.startPos == jump ) { + BranchNode node = parent; + do { + if( node.startOp == WasmBlockOperator.LOOP && node.startPos == jump ) { // the loop instruction itself doesn’t result in an iteration, instead a br 0 instruction causes the loop to repeat parent.add( new BranchNode( start, start, WasmBlockOperator.BR, null, deep ) ); // continue to the start of the loop return; } - parent = parent.parent; + node = node.parent; deep++; - } + } while( node != null ); throw new WasmException( "GOTO code without target loop/block. Jump from " + start + " to " + jump, gotoBlock.lineNumber ); } else { if( gotoBlock.nextPosition == jump ) { @@ -1384,6 +1379,12 @@ class BranchManager { parent = parent.parent; } + if( parent != null && parent.startOp == WasmBlockOperator.LOOP ) { + // a break in a LOOP is only a continue, we need to break to the outer block + deep++; + parent = parent.parent; + } + if( parent != null && parent.elseEndPos > gotoEndPos ) { // check if we break into an ELSE block which is possible in Java with a GOTO, occur with concatenated conditional operators diff --git a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java index ccc8769..1037c12 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java +++ b/test/de/inetsoftware/jwebassembly/runtime/ControlFlowOperators.java @@ -25,6 +25,7 @@ import de.inetsoftware.jwebassembly.ScriptEngine; import de.inetsoftware.jwebassembly.WasmRule; import de.inetsoftware.jwebassembly.api.annotation.Export; +@SuppressWarnings( { "javadoc", "null", "rawtypes", "cast", "boxing", "unused" } ) public class ControlFlowOperators extends AbstractBaseTest { @ClassRule @@ -55,6 +56,7 @@ public class ControlFlowOperators extends AbstractBaseTest { addParam( list, script, "whileLoopInElse_13" ); addParam( list, script, "whileLoopInElseAfterReturn" ); addParam( list, script, "whileLoopAfterIfWithReturn" ); + addParam( list, script, "whileLoopInsideLoop" ); addParam( list, script, "forLoop" ); addParam( list, script, "conditionalOperator" ); addParam( list, script, "conditionalOperator2" ); @@ -414,6 +416,26 @@ public class ControlFlowOperators extends AbstractBaseTest { return result; } + @Export + public static int whileLoopInsideLoop() { + int i = 15; + + MAIN: while( true ) { + while( i >= 9 ) { + i--; + } + int start = i; + + while( i > start ) { + if( true ) { + i--; + continue MAIN; + } + } + return start; + } + } + @Export static int forLoop() { int a = 0; diff --git a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java index e20ee39..0c07fe8 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java +++ b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java @@ -29,10 +29,10 @@ import org.junit.runners.Parameterized.Parameters; import de.inetsoftware.jwebassembly.ScriptEngine; import de.inetsoftware.jwebassembly.WasmRule; import de.inetsoftware.jwebassembly.api.annotation.Export; -import de.inetsoftware.jwebassembly.runtime.StructsGC.Abc2; import de.inetsoftware.jwebassembly.web.DOMString; import de.inetsoftware.jwebassembly.web.JSObject; +@SuppressWarnings( { "javadoc", "null", "rawtypes", "cast", "boxing" } ) public class StructsNonGC extends AbstractBaseTest { @ClassRule @@ -76,12 +76,11 @@ public class StructsNonGC extends AbstractBaseTest { addParam( list, script, "lambda1" ); addParam( list, script, "lambda2" ); addParam( list, script, "lambda3" ); -// TODO too many classes are loaded that are currently not yet compilable -// addParam( list, script, "simpleName_Object" ); -// addParam( list, script, "simpleName_Anonymous" ); -// addParam( list, script, "simpleName_Array" ); -// addParam( list, script, "simpleName_InnerClass" ); -// addParam( list, script, "simpleName_LocalClass" ); + addParam( list, script, "simpleName_Object" ); + addParam( list, script, "simpleName_Anonymous" ); + addParam( list, script, "simpleName_Array" ); + addParam( list, script, "simpleName_InnerClass" ); + addParam( list, script, "simpleName_LocalClass" ); addParam( list, script, "isPrimitive_int" ); addParam( list, script, "isPrimitive_Object" ); } @@ -251,42 +250,41 @@ public class StructsNonGC extends AbstractBaseTest { return JSObject.domString( clazz.getName() ); } -// TODO too many classes are loaded that are currently not yet compilable -// @Export -// static DOMString simpleName_Object() { -// Object obj = new Object(); -// Class clazz = obj.getClass(); -// return JSObject.domString( clazz.getSimpleName() ); -// } -// -// @Export -// static DOMString simpleName_Anonymous() { -// Object obj = new Object() {}; -// Class clazz = obj.getClass(); -// return JSObject.domString( clazz.getSimpleName() ); -// } -// -// @Export -// static DOMString simpleName_Array() { -// Object obj = new Object[0]; -// Class clazz = obj.getClass(); -// return JSObject.domString( clazz.getSimpleName() ); -// } -// -// @Export -// static DOMString simpleName_InnerClass() { -// Class clazz = TestClass.class; -// return JSObject.domString( clazz.getSimpleName() ); -// } -// -// @Export -// static DOMString simpleName_LocalClass() { -// class Foobar {} -// Object obj = new Foobar(); -// Class clazz = obj.getClass(); -// return JSObject.domString( clazz.getSimpleName() ); -// } -// + @Export + static DOMString simpleName_Object() { + Object obj = new Object(); + Class clazz = obj.getClass(); + return JSObject.domString( clazz.getSimpleName() ); + } + + @Export + static DOMString simpleName_Anonymous() { + Object obj = new Object() {}; + Class clazz = obj.getClass(); + return JSObject.domString( clazz.getSimpleName() ); + } + + @Export + static DOMString simpleName_Array() { + Object obj = new Object[0]; + Class clazz = obj.getClass(); + return JSObject.domString( clazz.getSimpleName() ); + } + + @Export + static DOMString simpleName_InnerClass() { + Class clazz = TestClass.class; + return JSObject.domString( clazz.getSimpleName() ); + } + + @Export + static DOMString simpleName_LocalClass() { + class Foobar {} + Object obj = new Foobar(); + Class clazz = obj.getClass(); + return JSObject.domString( clazz.getSimpleName() ); + } + @Export static boolean getComponentType() { Class clazz = byte.class; @@ -385,6 +383,7 @@ public class StructsNonGC extends AbstractBaseTest { static class Abc2 extends Abc { Abc2 abc; + @Override void bar() { a = 3; }