/* * Copyright 2018 - 2021 Volker Berlin (i-net software) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.inetsoftware.jwebassembly.runtime; import java.util.ArrayList; import java.util.Collection; import org.junit.ClassRule; import org.junit.runners.Parameterized.Parameters; import de.inetsoftware.jwebassembly.ScriptEngine; import de.inetsoftware.jwebassembly.WasmRule; import de.inetsoftware.jwebassembly.api.annotation.Export; public class ControlFlowOperators extends AbstractBaseTest { @ClassRule public static WasmRule rule = new WasmRule( TestClass.class ); public ControlFlowOperators( ScriptEngine script, String method, Object[] params ) { super( rule, script, method, params ); } @Parameters( name = "{0}-{1}" ) public static Collection data() { ArrayList list = new ArrayList<>(); for( ScriptEngine script : ScriptEngine.testEngines() ) { addParam( list, script, "ifeq" ); addParam( list, script, "ifne" ); addParam( list, script, "iflt" ); addParam( list, script, "ifMultiple" ); addParam( list, script, "ifMultipleDouble" ); addParam( list, script, "ifCompare" ); addParam( list, script, "switchDirect" ); addParam( list, script, "endlessLoop" ); addParam( list, script, "doWhileLoop" ); addParam( list, script, "doWhileLoopTwoConditions" ); addParam( list, script, "doWhileLoopWithBreak" ); addParam( list, script, "whileLoop" ); addParam( list, script, "forLoop" ); addParam( list, script, "conditionalOperator" ); addParam( list, script, "conditionalOperator2" ); addParam( list, script, "redifineVariable" ); addParam( list, script, "ifAnd_0" ); addParam( list, script, "ifAnd_3" ); addParam( list, script, "ifAnd_6" ); addParam( list, script, "if4And_6" ); addParam( list, script, "if4And_7" ); addParam( list, script, "ifOr0" ); addParam( list, script, "ifOr1" ); addParam( list, script, "ifOr3" ); addParam( list, script, "ifOr5" ); addParam( list, script, "ifOr7" ); addParam( list, script, "ifAndOr0" ); addParam( list, script, "ifAndOr2" ); addParam( list, script, "ifAndOr4" ); addParam( list, script, "ifAndOr6" ); addParam( list, script, "ifAndOr8" ); addParam( list, script, "ifAndOrComplex" ); addParam( list, script, "ifWithoutElseAndLoop" ); 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" ); addParam( list, script, "stringSwitchReverseFoo" ); addParam( list, script, "stringSwitchReverseBar" ); addParam( list, script, "stringSwitchReverseDefault" ); addParam( list, script, "stringSwitchContinue1" ); addParam( list, script, "stringSwitchContinue2" ); addParam( list, script, "stringSwitchContinueDefault" ); } rule.setTestParameters( list ); return list; } static class TestClass { @Export static int ifeq() { int condition = 0; if( condition != 0 ) { return 13; } else { return 76; } } @Export static int ifne() { int condition = 3; if( condition == 0 ) { return 13; } else { return 76; } } @Export static int iflt() { int condition = 3; if( condition >= 0 ) { condition = 13; } else { condition = 76; } return condition; } @Export static int ifMultiple() { int condition = 3; if( condition <= 0 ) { if( condition < 0 ) { condition = 13; } } else { if( condition > 0 ) { condition++; } else { condition--; } } if( condition > 2 ) { condition++; } else { condition = 0; } if( condition >= 2 ) { condition++; } else { condition = 0; } if( condition <= 123 ) { condition++; } else { condition = 0; } if( condition < 123 ) { condition++; } else { condition = 0; } if( condition != 123 ) { condition++; } else { condition = 0; } return condition; } @Export static int ifMultipleDouble() { double condition = 3; if( condition <= 0 ) { if( condition < 0 ) { condition = 13; } } else { if( condition > 0 ) { condition++; } else { condition--; } } if( condition > 2 ) { condition *= 2; } else { condition = 0; } if( condition >= 2 ) { condition *= 2; } else { condition = 0; } if( condition <= 123 ) { condition *= 2; } else { condition = 0; } if( condition < 123 ) { condition *= 2; } else { condition = 0; } if( condition != 123 ) { condition *= 2; } else { condition = 0; } if( condition == 123 ) { condition = 0; } else { condition *= 2; } int x = (int)(25 / condition); // prevent 0 as value return (int)condition; } @Export static int ifCompare() { double condition = 3.0; int result; if( condition >= 3.5 ) { result = 13; } else { result = 76; } return result; } @Export static int switchDirect() { return tableSwitch(10) + (tableSwitch( 9 ) * 10) + (tableSwitch( -1 ) * 100) + (lookupSwitch(Integer.MAX_VALUE) * 1000) + (lookupSwitch(0) * 10000 ); } private static int tableSwitch( int a ) { int b; switch( 1 + a - 1 ){ case 8: case 9: b = 2; break; case 10: case 11: b = 1; break; default: b = 9; } return b; } private static int lookupSwitch( int a ) { int b; switch(a){ case 1: b = 1; break; case 1000: case 1001: if( a == 1000 ) { b = 2; break; } else { b = 0; } //$FALL-THROUGH$ case Integer.MAX_VALUE: b = 3; break; default: b = 9; } return b; } @Export static int endlessLoop() { int a = 0; int b = 0; do { if( a < 10 ) { b++; } else { return a; } a++; } while( true ); } @Export static double doWhileLoop() { int a = 0; double d = 1.01; do { d *= 2; a++; } while( a < 10 ); return d; } @Export static int doWhileLoopTwoConditions() { int val = 42; int shift = 1; do { val >>>= shift; } while (val > 7 && shift > 0); return val; } @Export static double doWhileLoopWithBreak() { int a = 0; double d = 1.01; do { a++; if( a == 5 ) { break; } d *= 2; } while( a < 10 ); return a * d; } @Export static int whileLoop() { float a = 0; int b = 1; while( a < 10 ) { b *= 2; a++; } return b; } @Export static int forLoop() { int a = 0; for( int i = 0; i < 10; i++ ) { a += i; } return a; } @Export static int conditionalOperator () { int condition = 4; return condition >= 4 ? condition < 4 ? 1 : 2 : condition == 4 ? 3 : 4; } @Export static int conditionalOperator2() { int val = 42; int result = 3 + (val == 1 ? 1 : (val == 2 ? 2 : (val == 3 ? 3 : 4))); return result; } @Export static double redifineVariable() { int x = 42; if( x > 0 ) { double a = 1; double b = 2.5; return a + b; } else { int a = 1; int b = 3; return a + b; } } @Export static int ifAnd_0() { return ifAnd( 0 ); } @Export static int ifAnd_3() { return ifAnd( 3 ); } @Export static int ifAnd_6() { return ifAnd( 6 ); } private static int ifAnd( int condition ) { int result; if( condition > 0 && condition < 5 ) { result = 42; } else { result = 76; } return result; } @Export static int if4And_6() { return if4And( 6 ); } @Export static int if4And_7() { return if4And( 7 ); } private static int if4And( int condition ) { int result; if( condition > 1 && condition > 3 && condition > 5 && condition > 7 ) { result = 42; } else { result = 76; } return result; } @Export static int ifOr0() { return ifOr( 0 ); } @Export static int ifOr1() { return ifOr( 1 ); } @Export static int ifOr3() { return ifOr( 3 ); } @Export static int ifOr5() { return ifOr( 5 ); } @Export static int ifOr7() { return ifOr( 7 ); } private static int ifOr( int condition ) { int result; if( condition == 1 || condition == 3 || condition == 5 || condition == 7 ) { result = 42; } else { result = 76; } return result; } @Export static int ifAndOr0() { return ifAndOr( 0 ); } @Export static int ifAndOr2() { return ifAndOr( 2 ); } @Export static int ifAndOr4() { return ifAndOr( 4 ); } @Export static int ifAndOr6() { return ifAndOr( 6 ); } @Export static int ifAndOr8() { return ifAndOr( 8 ); } private static int ifAndOr( int condition ) { int result; if( (condition >= 1 && condition <= 3) || (condition >= 5 && condition <= 7) ) { result = 42; } else { result = 76; } return result; } @Export private static int ifAndOrComplex() { int b1 = 0; int b2 = 0; int result; if( (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || (b2 & 0xc0) != 0x80) { result = 13; } else { result = 42; } return result; } private static int ifWithoutElseAndLoop; @Export static int ifWithoutElseAndLoop() { int n = 1; // because some compiler (Eclipse) move the loop condition to the end of the loop. Then there can be an optimization that the if jump to the end of the loop. if( ifWithoutElseAndLoop != 1 ) { ifWithoutElseAndLoop = 1; } while( n < 10 ) { ifWithoutElseAndLoop *= n++; } return ifWithoutElseAndLoop; } @Export static int ifOrWithMulti() { int len = 4; // the GOTO before the ELSE is not related to the main IF condition if( (len == 4 || len == 9) ) { if( len == 9 ) { len = 13; } else { len = 42; } } return len; } @Export static int ifMultipleInsideThen() { int result = 0; if( (result == 7) || (result == 13) ) { // multiple IF inside the primary IF if( result == -1 ) { result = 1; } else { result = 2; } if( result > result ) { result = 3; } } else { result = 4; } return result; } @Export static int ifWithConditionalInsideThen() { int val = 42; int result = 0; if( val > 20 ) { if( val > 21 ) { result = val == 42 ? 4 : 5; } } else { result = 3; } 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" ); } @Export static int stringSwitchNormalBar() { return stringSwitchNormal( "bar" ); } @Export static int stringSwitchNormalDefault() { return stringSwitchNormal( "default" ); } private static int stringSwitchNormal( String tagName ) { switch( tagName ) { case "foo": return 1; case "bar": return 2; default: return 3; } } @Export static int stringSwitchReverseFoo() { return stringSwitchReverse( "foo" ); } @Export static int stringSwitchReverseBar() { return stringSwitchReverse( "bar" ); } @Export static int stringSwitchReverseDefault() { return stringSwitchReverse( "default" ); } private static int stringSwitchReverse( String tagName ) { switch( tagName ) { default: return 3; case "bar": return 2; case "foo": return 1; } } @Export static int stringSwitchContinue1() { return stringSwitchContinue( "1" ); } @Export static int stringSwitchContinue2() { return stringSwitchContinue( "2" ); } @Export static int stringSwitchContinueDefault() { return stringSwitchContinue( "8" ); } /** * Strings have continue hash codes that a compiler could use a tableswitch. */ private static int stringSwitchContinue( String tagName ) { switch( tagName ) { case "1": return 1; case "2": return 2; case "3": return 3; case "4": return 4; case "5": return 5; case "6": return 7; default: return 8; } } } }