/*
* 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 com.google.devtools.j2objc.translate;
import com.google.devtools.j2objc.GenerationTest;
import java.io.IOException;
/**
* Unit tests for {@link UnsequencedExpressionRewriter}.
*
* @author Keith Stanger
*/
public class UnsequencedExpressionRewriterTest extends GenerationTest {
@Override
protected void setUp() throws IOException {
super.setUp();
options.enableExtractUnsequencedModifications();
}
public void testUnsequencedPrefixExpression() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int i) { int j = ++i - ++i; } }", "Test", "Test.m");
assertTranslatedLines(translation,
"jint unseq$1 = ++i;",
"jint j = unseq$1 - ++i;");
}
public void testUnsequencedAssignmentExpression() throws IOException {
String translation = translateSourceFile(
"class Test { int test(int[] data, int i) { return data[i += 2] + i; } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"int unseq$1 = i += 2;",
"return IOSIntArray_Get(nil_chk(data), unseq$1) + i;");
}
public void testUnsequencedConditionalInfixExpression() throws IOException {
String translation = translateSourceFile(
"class Test { boolean test(int i) { "
+ "return i == 0 || i == 1 || ++i + i == 2 || i++ + i == 3 || i == 4; } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"jboolean unseq$1;",
"if (!(unseq$1 = (i == 0 || i == 1))) {",
" jint unseq$2 = ++i;",
" if (!(unseq$1 = (unseq$2 + i == 2))) {",
" jint unseq$3 = i++;",
" unseq$1 = (unseq$1 || unseq$3 + i == 3 || i == 4);",
" }",
"}",
"return unseq$1;");
}
public void testUnsequencedConditionalExpression() throws IOException {
String translation = translateSourceFile(
"class Test {"
+ " boolean test(int i) { return i == 0 ? i++ + i == 0 || i++ + i == 0 : ++i == 1; }"
+ " boolean test2(int i) { return i == 0 ? ++i == 1 : i++ + i == 0 || i++ + i == 0; } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"- (jboolean)testWithInt:(jint)i {",
" jboolean unseq$1;",
" if (i == 0) {",
" jint unseq$2 = i++;",
" jboolean unseq$3;",
" if (!(unseq$3 = (unseq$2 + i == 0))) {",
" jint unseq$4 = i++;",
" unseq$3 = (unseq$3 || unseq$4 + i == 0);",
" }",
" unseq$1 = unseq$3;",
" }",
" else {",
" unseq$1 = (++i == 1);",
" }",
" return unseq$1;",
"}");
assertTranslatedLines(translation,
"- (jboolean)test2WithInt:(jint)i {",
" jboolean unseq$1;",
" if (i == 0) {",
" unseq$1 = (++i == 1);",
" }",
" else {",
" jint unseq$2 = i++;",
" jboolean unseq$3;",
" if (!(unseq$3 = (unseq$2 + i == 0))) {",
" jint unseq$4 = i++;",
" unseq$3 = (unseq$3 || unseq$4 + i == 0);",
" }",
" unseq$1 = unseq$3;",
" }",
" return unseq$1;",
"}");
}
public void testWhileLoop() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int i) { while (i + i++ < 10) {} } }", "Test", "Test.m");
assertTranslatedLines(translation,
"while (true) {",
" jint unseq$1 = i;",
" if (!(unseq$1 + i++ < 10)) break;");
}
public void testVariableDeclarationStatementIsSplit() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { int i = 0, j = i++ + i, k = j, l = --k - k, m = 1; } }",
"Test", "Test.m");
if (options.isJDT()) {
assertTranslatedLines(translation,
"jint i = 0;",
"jint unseq$1 = i++;",
"jint j = unseq$1 + i, k = j;",
"jint unseq$2 = --k;",
"jint l = unseq$2 - k, m = 1;");
} else {
assertTranslatedLines(translation,
"jint i = 0;",
"jint unseq$1 = i++;",
"jint j = unseq$1 + i;",
"jint k = j;",
"jint unseq$2 = --k;",
"jint l = unseq$2 - k;",
"jint m = 1;");
}
}
public void testAssertStatement() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int i) { assert i++ + i++ == 0 : \"foo\" + i++ + i++; } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"jint unseq$1 = i++;",
"jboolean unseq$2 = unseq$1 + i++ == 0;",
"jint unseq$3 = i++;",
"JreAssert((unseq$2), (JreStrcat(\"$II\", @\"foo\", unseq$3, i++)));");
}
public void testForInitStatements() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { int i = 0, j = 0, k = 0; "
+ "for (i = i++ + i++, j = i++ + i++, k = i++ + i++;;) { } } }",
"Test", "Test.m");
if (options.isJDT()) {
assertTranslatedLines(translation,
"jint i = 0, j = 0, k = 0;",
"jint unseq$1 = i++;",
"jint unseq$2 = i++;",
"i = unseq$1 + unseq$2;",
"jint unseq$3 = i++;",
"j = unseq$3 + i++;",
"jint unseq$4 = i++;",
"for (k = unseq$4 + i++; ; ) {",
"}");
} else {
assertTranslatedLines(translation,
"jint i = 0;",
"jint j = 0;",
"jint k = 0;",
"jint unseq$1 = i++;",
"jint unseq$2 = i++;",
"i = unseq$1 + unseq$2;",
"jint unseq$3 = i++;",
"j = unseq$3 + i++;",
"jint unseq$4 = i++;",
"for (k = unseq$4 + i++; ; ) {",
"}");
}
}
public void testForInitWithDeclaration() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { int k = 0; "
+ "for (int i = k++ + k++, j = i++ + i++;;) { } } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"jint k = 0;",
"jint unseq$1 = k++;",
"jint i = unseq$1 + k++;",
"jint unseq$2 = i++;",
"for (jint j = unseq$2 + i++; ; ) {",
"}");
}
public void testIfConditionAndUpdaters() throws IOException {
String translation = translateSourceFile(
"class Test { void test() { int k = 0; "
+ "for (int i = k++ + k++; i++ + i++ < 10; i++, k = i++ + i++) { "
+ " String s = \"foo\" + i; } } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"jint k = 0;",
"jint unseq$1 = k++;",
"for (jint i = unseq$1 + k++; ; ) {",
" jint unseq$2 = i++;",
" if (!(unseq$2 + i++ < 10)) break;",
" NSString *s = JreStrcat(\"$I\", @\"foo\", i);",
" i++;",
" jint unseq$3 = i++;",
" k = unseq$3 + i++;",
"}");
}
public void testIfStatement() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int i) { "
+ "if (i++ + i++ == 0) {} else if (i++ + i++ == 1) {} else {} } }",
"Test", "Test.m");
assertTranslatedLines(translation,
"jint unseq$1 = i++;",
"if (unseq$1 + i++ == 0) {",
"}",
"else {",
" jint unseq$2 = i++;",
" if (unseq$2 + i++ == 1) {",
" }",
" else {",
" }",
"}");
}
public void testAssignToArray() throws IOException {
String translation = translateSourceFile(
"class Test { void test(int[] arr, int i) { arr[i] = i++; } }", "Test", "Test.m");
assertTranslatedLines(translation,
"jint unseq$1 = i;",
"*IOSIntArray_GetRef(nil_chk(arr), unseq$1) = i++;");
}
// Make sure that a conditional access remains conditional. Even if the access
// is not a modification, it might have been modified by the condition.
public void testConditionalAccess() throws IOException {
String translation = translateSourceFile(
"class Test { boolean foo(int i, int j) { return i < j; }"
+ " boolean test1(boolean b, int i) { return b || foo(i, i++); }"
+ " boolean test2(boolean b, int i) { return b ? foo(i, i++) : false; } }",
"Test", "Test.m");
// test1
assertTranslatedLines(translation,
"jboolean unseq$1;",
"if (!(unseq$1 = b)) {",
" jint unseq$2 = i;",
" unseq$1 = (unseq$1 || [self fooWithInt:unseq$2 withInt:i++]);",
"}",
"return unseq$1;");
// test2
assertTranslatedLines(translation,
"jboolean unseq$1;",
"if (b) {",
" jint unseq$2 = i;",
" unseq$1 = [self fooWithInt:unseq$2 withInt:i++];",
"}",
"else {",
" unseq$1 = false;",
"}",
"return unseq$1;");
}
// Instance variables do not appear to produce any unsequenced errors.
// Regression test for Issue #748.
public void testInstanceVarIsNotUnsequenced() throws IOException {
String translation = translateSourceFile(
"class Test { int i; void test() { this.i = this.i + this.i++; } }", "Test", "Test.m");
assertTranslation(translation, "self->i_ = self->i_ + self->i_++;");
}
}