<NME> UnsequencedExpressionRewriterTest.java
<BEF> /*
 * 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);",
        "  }",
        "}",
        "jboolean unseq$1;",
        "if (!(unseq$1 = (i == 0 || i == 1))) {",
        "  jint unseq$2 = ++i;",
        "  unseq$1 = (unseq$1 || unseq$2 + i == 2);",
        "}",
        "jboolean unseq$3;",
        "if (!(unseq$3 = unseq$1)) {",
        "  jint unseq$4 = i++;",
        "  unseq$3 = (unseq$3 || unseq$4 + i == 3);",
        "}",
        "return unseq$3 || i == 4;");
  }

  public void testUnsequencedConditionalExpression() throws IOException {
        "  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;");
  }

  // https://github.com/google/j2objc/issues/1487
  public void testMethodConditionalParameter() throws IOException {
    String translation =
        translateSourceFile(
            "class Test { \n"
                + "int test(Object o) { \n"
                + "int i = 42; \n"
                + "int j = -1; \n"
                // Depending on parameter expr execution order, either cmp(42, 84),
                // cmp(42, -2) or cmp(42, fn.applyAsInt(j, 2)) is executed.
                + "return cmp(j = i, (o == null) ? j : j * 2); }\n"
                + "int cmp(int a, int b) { return 0; }\n}",
            "Test",
            "Test.m");
    assertTranslatedLines(
        translation,
        "jint unseq$1 = j = i;",
        "return [self cmpWithInt:unseq$1 withInt:(o == nil) ? j : j * 2];");
  }

  // https://github.com/google/j2objc/issues/1487
  public void testConditionalsInsideNonConditionalInfixExpression() throws IOException {
    String translation =
        translateSourceFile(
            "class Test { \n"
                + "int test(byte[] input, byte[] tail, int tailLen, int p, int t) { \n"
                + "return (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |\n"
                + "     (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);\n}\n}",
            "Test",
            "Test.m");
    assertTranslatedLines(
        translation,
        "  jbyte unseq$1;",
        "if (tailLen > 1) {",
        "jint unseq$2 = t++;",
        "unseq$1 = IOSByteArray_Get(nil_chk(tail), unseq$2);",
        "}",
        "else {",
        "jint unseq$3 = p++;",
        "unseq$1 = IOSByteArray_Get(nil_chk(input), unseq$3);",
        "}",
        "return (JreLShift32(((unseq$1) & (jint) 0xff), 10)) "
            + "| (JreLShift32(((tailLen > 0 ? IOSByteArray_Get(nil_chk(tail), t++) "
            + ": IOSByteArray_Get(nil_chk(input), p++)) & (jint) 0xff), 2));");
  }

  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");
    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");
    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_++;");
  }
}

<MSG> Flatten deeply nested infix expressions to avoid a stack overflow.
	Change on 2015/03/10 by kstanger <kstanger@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=88214586

<DFF> @@ -64,14 +64,12 @@ public class UnsequencedExpressionRewriterTest extends GenerationTest {
         "jboolean unseq$1;",
         "if (!(unseq$1 = (i == 0 || i == 1))) {",
         "  jint unseq$2 = ++i;",
-        "  unseq$1 = (unseq$1 || unseq$2 + i == 2);",
-        "}",
-        "jboolean unseq$3;",
-        "if (!(unseq$3 = unseq$1)) {",
-        "  jint unseq$4 = i++;",
-        "  unseq$3 = (unseq$3 || unseq$4 + i == 3);",
+        "  if (!(unseq$1 = (unseq$2 + i == 2))) {",
+        "    jint unseq$3 = i++;",
+        "    unseq$1 = (unseq$1 || unseq$3 + i == 3 || i == 4);",
+        "  }",
         "}",
-        "return unseq$3 || i == 4;");
+        "return unseq$1;");
   }
 
   public void testUnsequencedConditionalExpression() throws IOException {

{'commit': 'diff --git a/translator/src/main/java/com/google/devtools/j2objc/ast/InfixExpression.java b/translator/src/main/java/com/google/devtools/j2objc/ast/InfixExpression.java\nindex e1b17a7b1..5535b8f2e 100644\n--- a/translator/src/main/java/com/google/devtools/j2objc/ast/InfixExpression.java\n+++ b/translator/src/main/java/com/google/devtools/j2objc/ast/InfixExpression.java\n@@ -14,6 +14,7 @@\n \n package com.google.devtools.j2objc.ast;\n \n+import com.google.common.collect.Lists;\n import com.google.common.collect.Maps;\n \n import org.eclipse.jdt.core.dom.ITypeBinding;\n@@ -88,10 +89,56 @@ public class InfixExpression extends Expression {\n     super(jdtNode);\n     typeBinding = jdtNode.resolveTypeBinding();\n     operator = Operator.fromJdtOperator(jdtNode.getOperator());\n-    leftOperand.set((Expression) TreeConverter.convert(jdtNode.getLeftOperand()));\n-    rightOperand.set((Expression) TreeConverter.convert(jdtNode.getRightOperand()));\n-    for (Object operand : jdtNode.extendedOperands()) {\n-      extendedOperands.add((Expression) TreeConverter.convert(operand));\n+\n+    // The JDT parser apparently does not always take advantage of extended\n+    // operands, resulting in potentially very deep trees that can overflow the\n+    // stack. This code traverses the subtree non-recursively and merges all\n+    // children that have the same operator into this node using extended\n+    // operands.\n+    List<Expression> operands = Lists.newArrayList();\n+    List<StackState> stack = Lists.newArrayList();\n+    stack.add(new StackState(jdtNode));\n+    while (!stack.isEmpty()) {\n+      StackState currentState = stack.get(stack.size() - 1);\n+      org.eclipse.jdt.core.dom.Expression child = currentState.nextChild();\n+      if (child == null) {\n+        stack.remove(stack.size() - 1);\n+        continue;\n+      }\n+      if (child instanceof org.eclipse.jdt.core.dom.InfixExpression) {\n+        org.eclipse.jdt.core.dom.InfixExpression infixChild =\n+            (org.eclipse.jdt.core.dom.InfixExpression) child;\n+        if (infixChild.getOperator().equals(jdtNode.getOperator())) {\n+          stack.add(new StackState(infixChild));\n+          continue;\n+        }\n+      }\n+      operands.add((Expression) TreeConverter.convert(child));\n+    }\n+    leftOperand.set(operands.get(0));\n+    rightOperand.set(operands.get(1));\n+    extendedOperands.addAll(operands.subList(2, operands.size()));\n+  }\n+\n+  private static class StackState {\n+    private final org.eclipse.jdt.core.dom.InfixExpression expression;\n+    private int nextChild = -2;\n+\n+    private StackState(org.eclipse.jdt.core.dom.InfixExpression expr) {\n+      expression = expr;\n+    }\n+\n+    private org.eclipse.jdt.core.dom.Expression nextChild() {\n+      int childIdx = nextChild++;\n+      if (childIdx == -2) {\n+        return expression.getLeftOperand();\n+      } else if (childIdx == -1) {\n+        return expression.getRightOperand();\n+      } else if (childIdx < expression.extendedOperands().size()) {\n+        return (org.eclipse.jdt.core.dom.Expression) expression.extendedOperands().get(childIdx);\n+      } else {\n+        return null;\n+      }\n     }\n   }\n \ndiff --git a/translator/src/main/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriter.java b/translator/src/main/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriter.java\nindex 041687ca8..bfc7d64de 100644\n--- a/translator/src/main/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriter.java\n+++ b/translator/src/main/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriter.java\n@@ -350,7 +350,7 @@ public class UnsequencedExpressionRewriter extends TreeVisitor {\n       InfixExpression result = new InfixExpression(\n           Types.resolveJavaType("boolean"), op, branches.get(0).copy(), branches.get(1).copy());\n       for (int i = 2; i < branches.size(); i++) {\n-        result.getExtendedOperands().add(branches.get(i));\n+        result.getExtendedOperands().add(branches.get(i).copy());\n       }\n       return result;\n     }\ndiff --git a/translator/src/test/java/com/google/devtools/j2objc/ast/TreeConvertTest.java b/translator/src/test/java/com/google/devtools/j2objc/ast/TreeConvertTest.java\nindex 3276b69a0..8c9c0e65c 100644\n--- a/translator/src/test/java/com/google/devtools/j2objc/ast/TreeConvertTest.java\n+++ b/translator/src/test/java/com/google/devtools/j2objc/ast/TreeConvertTest.java\n@@ -51,4 +51,17 @@ public class TreeConvertTest extends GenerationTest {\n         + "Simple[] value(); }", "Elements", "Elements.h");\n     assertTranslation(translation, "@property (readonly) IOSObjectArray *value;");\n   }\n+\n+  public void testVeryDeeplyNextedExpression() throws IOException {\n+    StringBuilder sb = new StringBuilder("return i == -1");\n+    // Create a 2000 node deep infix expression.\n+    for (int i = 0; i < 2000; i++) {\n+      sb.append(" || i == " + i);\n+    }\n+    sb.append(";");\n+    String exprStr = sb.toString();\n+    String translation = translateSourceFile(String.format(\n+        "class Test { boolean test(int i) { %s } }", exprStr), "Test", "Test.m");\n+    assertTranslation(translation, exprStr);\n+  }\n }\ndiff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/RewriterTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/RewriterTest.java\nindex 771ff6bfa..3dad41d57 100644\n--- a/translator/src/test/java/com/google/devtools/j2objc/translate/RewriterTest.java\n+++ b/translator/src/test/java/com/google/devtools/j2objc/translate/RewriterTest.java\n@@ -361,7 +361,7 @@ public class RewriterTest extends GenerationTest {\n         "Test", "Test.m");\n     assertTranslation(translation, "return a & b;");\n     assertTranslation(translation, "return c | d;");\n-    assertTranslatedLines(translation, "return ((e & f) | (g & h)) | i;");\n+    assertTranslatedLines(translation, "return (e & f) | (g & h) | i;");\n     assertTranslatedLines(translation, "return j | k | (l & m & n);");\n   }\n \ndiff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java\nindex 7924061ce..9ef4af9ae 100644\n--- a/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java\n+++ b/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java\n@@ -64,14 +64,12 @@ public class UnsequencedExpressionRewriterTest extends GenerationTest {\n         "jboolean unseq$1;",\n         "if (!(unseq$1 = (i == 0 || i == 1))) {",\n         "  jint unseq$2 = ++i;",\n-        "  unseq$1 = (unseq$1 || unseq$2 + i == 2);",\n-        "}",\n-        "jboolean unseq$3;",\n-        "if (!(unseq$3 = unseq$1)) {",\n-        "  jint unseq$4 = i++;",\n-        "  unseq$3 = (unseq$3 || unseq$4 + i == 3);",\n+        "  if (!(unseq$1 = (unseq$2 + i == 2))) {",\n+        "    jint unseq$3 = i++;",\n+        "    unseq$1 = (unseq$1 || unseq$3 + i == 3 || i == 4);",\n+        "  }",\n         "}",\n-        "return unseq$3 || i == 4;");\n+        "return unseq$1;");\n   }\n \n   public void testUnsequencedConditionalExpression() throws IOException {\n', 'commit_subject': 'Flatten deeply nested infix expressions to avoid a stack overflow. \tChange on 2015/03/10 by kstanger <kstanger@google.com> ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=88214586', 'repo_name': 'google/j2objc', 'committer': {'name': 'Keith Stanger', 'email': '49783279543e4289ca1f85c30d71c6f27f26fca0@google.com', 'time_sec': '1426707573', 'tz_offset': '-240', 'date': {'seconds': '1426707573'}}, 'commit_tree': '1b2fe4184c492f143380988cda53c713ede2d14f', 'commit_message': 'Flatten deeply nested infix expressions to avoid a stack overflow.\n\tChange on 2015/03/10 by kstanger <kstanger@google.com>\n-------------\nCreated by MOE: http://code.google.com/p/moe-java\nMOE_MIGRATED_REVID=88214586\n', 'language_name': 'HTML', 'license': 'apache-2.0', 'hunks': '@@ -64,14 +64,12 @@ public class UnsequencedExpressionRewriterTest extends GenerationTest {\n         "jboolean unseq$1;",\n         "if (!(unseq$1 = (i == 0 || i == 1))) {",\n         "  jint unseq$2 = ++i;",\n-        "  unseq$1 = (unseq$1 || unseq$2 + i == 2);",\n-        "}",\n-        "jboolean unseq$3;",\n-        "if (!(unseq$3 = unseq$1)) {",\n-        "  jint unseq$4 = i++;",\n-        "  unseq$3 = (unseq$3 || unseq$4 + i == 3);",\n+        "  if (!(unseq$1 = (unseq$2 + i == 2))) {",\n+        "    jint unseq$3 = i++;",\n+        "    unseq$1 = (unseq$1 || unseq$3 + i == 3 || i == 4);",\n+        "  }",\n         "}",\n-        "return unseq$3 || i == 4;");\n+        "return unseq$1;");\n   }\n \n   public void testUnsequencedConditionalExpression() throws IOException {\n', 'addition_count': 5, 'deletion_count': 7, 'src_file': 'a/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java', 'tgt_file': 'b/translator/src/test/java/com/google/devtools/j2objc/translate/UnsequencedExpressionRewriterTest.java', 'file_extension': '.java', 'before_file': '/*\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.devtools.j2objc.translate;\n\nimport com.google.devtools.j2objc.GenerationTest;\nimport java.io.IOException;\n\n/**\n * Unit tests for {@link UnsequencedExpressionRewriter}.\n *\n * @author Keith Stanger\n */\npublic class UnsequencedExpressionRewriterTest extends GenerationTest {\n\n  @Override\n  protected void setUp() throws IOException {\n    super.setUp();\n    options.enableExtractUnsequencedModifications();\n  }\n\n  public void testUnsequencedPrefixExpression() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test(int i) { int j = ++i - ++i; } }", "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint unseq$1 = ++i;",\n        "jint j = unseq$1 - ++i;");\n  }\n\n  public void testUnsequencedAssignmentExpression() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { int test(int[] data, int i) { return data[i += 2] + i; } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "int unseq$1 = i += 2;",\n        "return IOSIntArray_Get(nil_chk(data), unseq$1) + i;");\n  }\n\n  public void testUnsequencedConditionalInfixExpression() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { boolean test(int i) { "\n        + "return i == 0 || i == 1 || ++i + i == 2 || i++ + i == 3 || i == 4; } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jboolean unseq$1;",\n        "if (!(unseq$1 = (i == 0 || i == 1))) {",\n        "  jint unseq$2 = ++i;",\n        "  if (!(unseq$1 = (unseq$2 + i == 2))) {",\n        "    jint unseq$3 = i++;",\n        "    unseq$1 = (unseq$1 || unseq$3 + i == 3 || i == 4);",\n        "  }",\n        "}",\n        "jboolean unseq$1;",\n        "if (!(unseq$1 = (i == 0 || i == 1))) {",\n        "  jint unseq$2 = ++i;",\n        "  unseq$1 = (unseq$1 || unseq$2 + i == 2);",\n        "}",\n        "jboolean unseq$3;",\n        "if (!(unseq$3 = unseq$1)) {",\n        "  jint unseq$4 = i++;",\n        "  unseq$3 = (unseq$3 || unseq$4 + i == 3);",\n        "}",\n        "return unseq$3 || i == 4;");\n  }\n\n  public void testUnsequencedConditionalExpression() throws IOException {\n        "  if (i == 0) {",\n        "    jint unseq$2 = i++;",\n        "    jboolean unseq$3;",\n        "    if (!(unseq$3 = (unseq$2 + i == 0))) {",\n        "      jint unseq$4 = i++;",\n        "      unseq$3 = (unseq$3 || unseq$4 + i == 0);",\n        "    }",\n        "    unseq$1 = unseq$3;",\n        "  }",\n        "  else {",\n        "    unseq$1 = (++i == 1);",\n        "  }",\n        "  return unseq$1;",\n        "}");\n    assertTranslatedLines(translation,\n        "- (jboolean)test2WithInt:(jint)i {",\n        "  jboolean unseq$1;",\n        "  if (i == 0) {",\n        "    unseq$1 = (++i == 1);",\n        "  }",\n        "  else {",\n        "    jint unseq$2 = i++;",\n        "    jboolean unseq$3;",\n        "    if (!(unseq$3 = (unseq$2 + i == 0))) {",\n        "      jint unseq$4 = i++;",\n        "      unseq$3 = (unseq$3 || unseq$4 + i == 0);",\n        "    }",\n        "    unseq$1 = unseq$3;",\n        "  }",\n        "  return unseq$1;",\n        "}");\n  }\n\n  public void testWhileLoop() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test(int i) { while (i + i++ < 10) {} } }", "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "while (true) {",\n        "  jint unseq$1 = i;",\n        "  if (!(unseq$1 + i++ < 10)) break;");\n  }\n\n  // https://github.com/google/j2objc/issues/1487\n  public void testMethodConditionalParameter() throws IOException {\n    String translation =\n        translateSourceFile(\n            "class Test { \\n"\n                + "int test(Object o) { \\n"\n                + "int i = 42; \\n"\n                + "int j = -1; \\n"\n                // Depending on parameter expr execution order, either cmp(42, 84),\n                // cmp(42, -2) or cmp(42, fn.applyAsInt(j, 2)) is executed.\n                + "return cmp(j = i, (o == null) ? j : j * 2); }\\n"\n                + "int cmp(int a, int b) { return 0; }\\n}",\n            "Test",\n            "Test.m");\n    assertTranslatedLines(\n        translation,\n        "jint unseq$1 = j = i;",\n        "return [self cmpWithInt:unseq$1 withInt:(o == nil) ? j : j * 2];");\n  }\n\n  // https://github.com/google/j2objc/issues/1487\n  public void testConditionalsInsideNonConditionalInfixExpression() throws IOException {\n    String translation =\n        translateSourceFile(\n            "class Test { \\n"\n                + "int test(byte[] input, byte[] tail, int tailLen, int p, int t) { \\n"\n                + "return (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |\\n"\n                + "     (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);\\n}\\n}",\n            "Test",\n            "Test.m");\n    assertTranslatedLines(\n        translation,\n        "  jbyte unseq$1;",\n        "if (tailLen > 1) {",\n        "jint unseq$2 = t++;",\n        "unseq$1 = IOSByteArray_Get(nil_chk(tail), unseq$2);",\n        "}",\n        "else {",\n        "jint unseq$3 = p++;",\n        "unseq$1 = IOSByteArray_Get(nil_chk(input), unseq$3);",\n        "}",\n        "return (JreLShift32(((unseq$1) & (jint) 0xff), 10)) "\n            + "| (JreLShift32(((tailLen > 0 ? IOSByteArray_Get(nil_chk(tail), t++) "\n            + ": IOSByteArray_Get(nil_chk(input), p++)) & (jint) 0xff), 2));");\n  }\n\n  public void testVariableDeclarationStatementIsSplit() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test() { int i = 0, j = i++ + i, k = j, l = --k - k, m = 1; } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint i = 0;",\n        "jint unseq$1 = i++;",\n        "jint j = unseq$1 + i;",\n        "jint k = j;",\n        "jint unseq$2 = --k;",\n        "jint l = unseq$2 - k;",\n        "jint m = 1;");\n  }\n\n  public void testAssertStatement() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test(int i) { assert i++ + i++ == 0 : \\"foo\\" + i++ + i++; } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint unseq$1 = i++;",\n        "jboolean unseq$2 = unseq$1 + i++ == 0;",\n        "jint unseq$3 = i++;",\n        "JreAssert(unseq$2, JreStrcat(\\"$II\\", @\\"foo\\", unseq$3, i++));");\n  }\n\n  public void testForInitStatements() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test() { int i = 0, j = 0, k = 0; "\n        + "for (i = i++ + i++, j = i++ + i++, k = i++ + i++;;) { } } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint i = 0;",\n        "jint j = 0;",\n        "jint k = 0;",\n        "jint unseq$1 = i++;",\n        "jint unseq$2 = i++;",\n        "i = unseq$1 + unseq$2;",\n        "jint unseq$3 = i++;",\n        "j = unseq$3 + i++;",\n        "jint unseq$4 = i++;",\n        "for (k = unseq$4 + i++; ; ) {",\n        "}");\n  }\n\n  public void testForInitWithDeclaration() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test() { int k = 0; "\n        + "for (int i = k++ + k++, j = i++ + i++;;) { } } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint k = 0;",\n        "jint unseq$1 = k++;",\n        "jint i = unseq$1 + k++;",\n        "jint unseq$2 = i++;",\n        "for (jint j = unseq$2 + i++; ; ) {",\n        "}");\n  }\n\n  public void testIfConditionAndUpdaters() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test() { int k = 0; "\n        + "for (int i = k++ + k++; i++ + i++ < 10; i++, k = i++ + i++) { "\n        + "  String s = \\"foo\\" + i; } } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint k = 0;",\n        "jint unseq$1 = k++;",\n        "for (jint i = unseq$1 + k++; ; ) {",\n        "  jint unseq$2 = i++;",\n        "  if (!(unseq$2 + i++ < 10)) break;",\n        "  NSString *s = JreStrcat(\\"$I\\", @\\"foo\\", i);",\n        "  i++;",\n        "  jint unseq$3 = i++;",\n        "  k = unseq$3 + i++;",\n        "}");\n  }\n\n  public void testIfStatement() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test(int i) { "\n        + "if (i++ + i++ == 0) {} else if (i++ + i++ == 1) {} else {} } }",\n        "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint unseq$1 = i++;",\n        "if (unseq$1 + i++ == 0) {",\n        "}",\n        "else {",\n        "  jint unseq$2 = i++;",\n        "  if (unseq$2 + i++ == 1) {",\n        "  }",\n        "  else {",\n        "  }",\n        "}");\n  }\n\n  public void testAssignToArray() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { void test(int[] arr, int i) { arr[i] = i++; } }", "Test", "Test.m");\n    assertTranslatedLines(translation,\n        "jint unseq$1 = i;",\n        "*IOSIntArray_GetRef(nil_chk(arr), unseq$1) = i++;");\n  }\n\n  // Make sure that a conditional access remains conditional. Even if the access\n  // is not a modification, it might have been modified by the condition.\n  public void testConditionalAccess() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { boolean foo(int i, int j) { return i < j; }"\n        + " boolean test1(boolean b, int i) { return b || foo(i, i++); }"\n        + " boolean test2(boolean b, int i) { return b ? foo(i, i++) : false; } }",\n        "Test", "Test.m");\n    // test1\n    assertTranslatedLines(translation,\n        "jboolean unseq$1;",\n        "if (!(unseq$1 = b)) {",\n        "  jint unseq$2 = i;",\n        "  unseq$1 = (unseq$1 || [self fooWithInt:unseq$2 withInt:i++]);",\n        "}",\n        "return unseq$1;");\n    // test2\n    assertTranslatedLines(translation,\n        "jboolean unseq$1;",\n        "if (b) {",\n        "  jint unseq$2 = i;",\n        "  unseq$1 = [self fooWithInt:unseq$2 withInt:i++];",\n        "}",\n        "else {",\n        "  unseq$1 = false;",\n        "}",\n        "return unseq$1;");\n  }\n\n  // Instance variables do not appear to produce any unsequenced errors.\n  // Regression test for Issue #748.\n  public void testInstanceVarIsNotUnsequenced() throws IOException {\n    String translation = translateSourceFile(\n        "class Test { int i; void test() { this.i = this.i + this.i++; } }", "Test", "Test.m");\n    assertTranslation(translation, "self->i_ = self->i_ + self->i_++;");\n  }\n}\n', 'message': 'Flatten deeply nested infix expressions to avoid a stack overflow.\n\tChange on 2015/03/10 by kstanger <kstanger@google.com>\n-------------\nCreated by MOE: http://code.google.com/p/moe-java\nMOE_MIGRATED_REVID=88214586\n', 'author_name': 'Keith Stanger', 'lang': 'java'}