diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index e8908ff52d20..8cb2b0c221b6 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -243,6 +243,7 @@ public class MethodProcessorRunnable implements Runnable { varProc.setVarDefinitions(root); cancellationManager.checkCanceled(); + SecondaryFunctionsHelper.updateAssignments(root); // must be the last invocation, because it makes the statement structure inconsistent // FIXME: new edge type needed diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index 4a1ad182cf4a..5f53e116b874 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -417,4 +417,46 @@ public final class SecondaryFunctionsHelper { return null; } + + // Updates assignments to make them compound assignments if possible + public static void updateAssignments(Statement stat) { + List objects = new ArrayList<>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents()); + + for (Object obj : objects) { + if (obj instanceof Statement) { + updateAssignments((Statement) obj); + } else if (obj instanceof Exprent) { + Exprent exprent = (Exprent) obj; + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent assignment = (AssignmentExprent) exprent; + + List params = exprent.getAllExprents(); + + Exprent lhs = params.get(0); + Exprent rhs = params.get(1); + + // Check for expressions that are standard assignments where the left hand side is a variable and the right hand side is a function + if (assignment.getCondType() == -1 && lhs.type == Exprent.EXPRENT_VAR && rhs.type == Exprent.EXPRENT_FUNCTION) { + VarExprent lhsVar = (VarExprent) lhs; + FunctionExprent rhsFunc = (FunctionExprent) rhs; + + List funcParams = rhsFunc.getAllExprents(); + + // Make sure that the function is a mathematical or bitwise function and function's lhs is a variable + if (rhsFunc.getFuncType() <= FunctionExprent.FUNCTION_USHR && funcParams.get(0).type == Exprent.EXPRENT_VAR) { + VarExprent lhsVarFunc = (VarExprent) funcParams.get(0); + + // Check if the left hand side of the assignment and the left hand side of the function are the same variable + if (lhsVar.getIndex() == lhsVarFunc.getIndex()) { + // If all the checks succeed, set the assignment to be a compound assignment and set the right to the right hand side of the function + assignment.setCondType(rhsFunc.getFuncType()); + assignment.setRight(funcParams.get(1)); + } + } + } + } + } + } + } } diff --git a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java index baa08475e614..fe56f736d6f3 100644 --- a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -242,9 +242,8 @@ public class SingleClassesTest extends SingleClassesTestBase { @Test(expected = ClassFormatException.class) public void testUnsupportedConstantPoolEntry() { doTest("java11/TestUnsupportedConstantPoolEntry"); } - @Test public void testSwitchOnStatic() { doTest("pkg/SwitchOnStatic"); } - + @Test public void testCompoundAssignment() { doTest("pkg/TestCompoundAssignment"); } @Test public void testTryToPreserveCast() { doTest("pkg/TryToPreserveCast"); } @Test public void testPreserveAssignmentToRecord() { doTest("pkg/PreserveAssignmentToRecord"); } diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestCompoundAssignment.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestCompoundAssignment.class index 7ce9ef3d3240..8a940dae3be9 100644 Binary files a/plugins/java-decompiler/engine/testData/classes/pkg/TestCompoundAssignment.class and b/plugins/java-decompiler/engine/testData/classes/pkg/TestCompoundAssignment.class differ diff --git a/plugins/java-decompiler/engine/testData/results/TestCompoundAssignment.dec b/plugins/java-decompiler/engine/testData/results/TestCompoundAssignment.dec index 3bd137be8055..ef6bd4a14465 100644 --- a/plugins/java-decompiler/engine/testData/results/TestCompoundAssignment.dec +++ b/plugins/java-decompiler/engine/testData/results/TestCompoundAssignment.dec @@ -1,74 +1,69 @@ package pkg; public class TestCompoundAssignment { - public int testSimple(int i, int j) { - i += j;// 5 - i -= j;// 6 - i *= j;// 7 - i /= j;// 8 - i &= j;// 9 - i |= j;// 10 - i ^= j;// 11 - i >>= j;// 12 - i <<= j;// 13 - i >>>= j;// 14 - return i;// 16 + public int testSimple(int var1, int var2) { + var1 += var2;// 5 + var1 -= var2;// 6 + var1 *= var2;// 7 + var1 /= var2;// 8 + var1 &= var2;// 9 + var1 |= var2;// 10 + var1 ^= var2;// 11 + var1 >>= var2;// 12 + var1 <<= var2;// 13 + return var1 >>> var2;// 14 16 } - public int testComplex(int i, int j, int k) { - i += j + k;// 20 - i -= j + k;// 21 - i *= j + k;// 22 - i /= j + k;// 23 - i &= j + k;// 24 - i |= j + k;// 25 - i ^= j + k;// 26 - i >>= j + k;// 27 - i <<= j + k;// 28 - i >>>= j + k;// 29 - return i;// 31 + public int testComplex(int var1, int var2, int var3) { + var1 += var2 + var3;// 20 + var1 -= var2 + var3;// 21 + var1 *= var2 + var3;// 22 + var1 /= var2 + var3;// 23 + var1 &= var2 + var3;// 24 + var1 |= var2 + var3;// 25 + var1 ^= var2 + var3;// 26 + var1 >>= var2 + var3;// 27 + var1 <<= var2 + var3;// 28 + return var1 >>> var2 + var3;// 29 31 } - public int testComplexParens(int i, int j, int k, int m) { - i += (j + k) * m;// 35 - i -= (j + k) * m;// 36 - i *= (j + k) * m;// 37 - i /= (j + k) * m;// 38 - i &= (j + k) * m;// 39 - i |= (j + k) * m;// 40 - i ^= (j + k) * m;// 41 - i >>= (j + k) * m;// 42 - i <<= (j + k) * m;// 43 - i >>>= (j + k) * m;// 44 - return i;// 46 + public int testComplexParens(int var1, int var2, int var3, int var4) { + var1 += (var2 + var3) * var4;// 35 + var1 -= (var2 + var3) * var4;// 36 + var1 *= (var2 + var3) * var4;// 37 + var1 /= (var2 + var3) * var4;// 38 + var1 &= (var2 + var3) * var4;// 39 + var1 |= (var2 + var3) * var4;// 40 + var1 ^= (var2 + var3) * var4;// 41 + var1 >>= (var2 + var3) * var4;// 42 + var1 <<= (var2 + var3) * var4;// 43 + return var1 >>> (var2 + var3) * var4;// 44 46 } - public int testComplexTernary(int i, int j, int k, int m, boolean b) { - i += b ? j : k * m;// 50 - i -= b ? j : k * m;// 51 - i *= b ? j : k * m;// 52 - i /= b ? j : k * m;// 53 - i &= b ? j : k * m;// 54 - i |= b ? j : k * m;// 55 - i ^= b ? j : k * m;// 56 - i >>= b ? j : k * m;// 57 - i <<= b ? j : k * m;// 58 - i >>>= b ? j : k * m;// 59 - return i;// 61 + public int testComplexTernary(int var1, int var2, int var3, int var4, boolean var5) { + var1 += var5 ? var2 : var3 * var4;// 50 + var1 -= var5 ? var2 : var3 * var4;// 51 + var1 *= var5 ? var2 : var3 * var4;// 52 + var1 /= var5 ? var2 : var3 * var4;// 53 + var1 &= var5 ? var2 : var3 * var4;// 54 + var1 |= var5 ? var2 : var3 * var4;// 55 + var1 ^= var5 ? var2 : var3 * var4;// 56 + var1 >>= var5 ? var2 : var3 * var4;// 57 + var1 <<= var5 ? var2 : var3 * var4;// 58 + return var1 >>> (var5 ? var2 : var3 * var4);// 59 61 } - public int testArrayOp(int i, int j, int[] a, int b) { - i += a[b] = j;// 65 - i -= a[b] = j;// 66 - i *= a[b] = j;// 67 - i /= a[b] = j;// 68 - i &= a[b] = j;// 69 - i |= a[b] = j;// 70 - i ^= a[b] = j;// 71 - i >>= a[b] = j;// 72 - i <<= a[b] = j;// 73 - i >>>= a[b] = j;// 74 - return i;// 76 + public int testArrayOp(int var1, int var2, int[] var3, int var4) { + var1 += var3[var4] = var2;// 65 + var1 -= var3[var4] = var2;// 66 + var1 *= var3[var4] = var2;// 67 + var1 /= var3[var4] = var2;// 68 + var1 &= var3[var4] = var2;// 69 + var1 |= var3[var4] = var2;// 70 + var1 ^= var3[var4] = var2;// 71 + var1 >>= var3[var4] = var2;// 72 + var1 <<= var3[var4] = var2;// 73 + return var1 >>> (var3[var4] = var2);// 74 76 } } @@ -92,290 +87,290 @@ class 'pkg/TestCompoundAssignment' { 1f 11 21 12 23 12 + 24 13 25 13 - 27 13 - 28 14 - 29 14 + 26 13 + 29 13 } method 'testComplex (III)I' { - 1 18 - 2 18 - 3 18 - 5 18 - 7 19 - 8 19 - 9 19 - b 19 - d 20 - e 20 - f 20 - 11 20 - 13 21 - 14 21 - 15 21 - 17 21 - 19 22 - 1a 22 - 1b 22 - 1d 22 - 1f 23 - 20 23 - 21 23 - 23 23 - 25 24 - 26 24 - 27 24 - 29 24 - 2b 25 - 2c 25 - 2d 25 - 2f 25 - 31 26 - 32 26 - 33 26 - 35 26 - 37 27 - 38 27 - 39 27 - 3b 27 - 3c 28 - 3d 28 + 1 17 + 2 17 + 3 17 + 5 17 + 7 18 + 8 18 + 9 18 + b 18 + d 19 + e 19 + f 19 + 11 19 + 13 20 + 14 20 + 15 20 + 17 20 + 19 21 + 1a 21 + 1b 21 + 1d 21 + 1f 22 + 20 22 + 21 22 + 23 22 + 25 23 + 26 23 + 27 23 + 29 23 + 2b 24 + 2c 24 + 2d 24 + 2f 24 + 31 25 + 32 25 + 33 25 + 35 25 + 36 26 + 37 26 + 38 26 + 39 26 + 3a 26 + 3d 26 } method 'testComplexParens (IIII)I' { - 1 32 - 2 32 - 3 32 - 4 32 - 5 32 - 6 32 - 8 32 - a 33 - b 33 - c 33 - d 33 - e 33 - f 33 - 11 33 - 13 34 - 14 34 - 15 34 - 16 34 - 17 34 - 18 34 - 1a 34 - 1c 35 - 1d 35 - 1e 35 - 1f 35 - 20 35 - 21 35 - 23 35 - 25 36 - 26 36 - 27 36 - 28 36 - 29 36 - 2a 36 - 2c 36 - 2e 37 - 2f 37 - 30 37 - 31 37 - 32 37 - 33 37 - 35 37 - 37 38 - 38 38 - 39 38 - 3a 38 - 3b 38 - 3c 38 - 3e 38 - 40 39 - 41 39 - 42 39 - 43 39 - 44 39 - 45 39 - 47 39 - 49 40 - 4a 40 - 4b 40 - 4c 40 - 4d 40 - 4e 40 - 50 40 - 52 41 - 53 41 - 54 41 - 55 41 - 56 41 - 57 41 - 59 41 - 5a 42 - 5b 42 + 1 30 + 2 30 + 3 30 + 4 30 + 5 30 + 6 30 + 8 30 + a 31 + b 31 + c 31 + d 31 + e 31 + f 31 + 11 31 + 13 32 + 14 32 + 15 32 + 16 32 + 17 32 + 18 32 + 1a 32 + 1c 33 + 1d 33 + 1e 33 + 1f 33 + 20 33 + 21 33 + 23 33 + 25 34 + 26 34 + 27 34 + 28 34 + 29 34 + 2a 34 + 2c 34 + 2e 35 + 2f 35 + 30 35 + 31 35 + 32 35 + 33 35 + 35 35 + 37 36 + 38 36 + 39 36 + 3a 36 + 3b 36 + 3c 36 + 3e 36 + 40 37 + 41 37 + 42 37 + 43 37 + 44 37 + 45 37 + 47 37 + 49 38 + 4a 38 + 4b 38 + 4c 38 + 4d 38 + 4e 38 + 50 38 + 51 39 + 52 39 + 53 39 + 54 39 + 55 39 + 56 39 + 57 39 + 58 39 + 5b 39 } method 'testComplexTernary (IIIIZ)I' { - 1 46 - 2 46 - 3 46 - 6 46 - a 46 - b 46 - c 46 - d 46 - f 46 - 11 47 - 12 47 - 13 47 - 16 47 - 1a 47 - 1b 47 - 1c 47 - 1d 47 - 1f 47 - 21 48 - 22 48 - 23 48 - 26 48 - 2a 48 - 2b 48 - 2c 48 - 2d 48 - 2f 48 - 31 49 - 32 49 - 33 49 - 36 49 - 3a 49 - 3b 49 - 3c 49 - 3d 49 - 3f 49 - 41 50 - 42 50 - 43 50 - 46 50 - 4a 50 - 4b 50 - 4c 50 - 4d 50 - 4f 50 - 51 51 - 52 51 - 53 51 - 56 51 - 5a 51 - 5b 51 - 5c 51 - 5d 51 - 5f 51 - 61 52 - 62 52 - 63 52 - 66 52 - 6a 52 - 6b 52 - 6c 52 - 6d 52 - 6f 52 - 71 53 - 72 53 - 73 53 - 76 53 - 7a 53 - 7b 53 - 7c 53 - 7d 53 - 7f 53 - 81 54 - 82 54 - 83 54 - 86 54 - 8a 54 - 8b 54 - 8c 54 - 8d 54 - 8f 54 - 91 55 - 92 55 - 93 55 - 96 55 - 9a 55 - 9b 55 - 9c 55 - 9d 55 - 9f 55 - a0 56 - a1 56 + 1 43 + 2 43 + 3 43 + 6 43 + a 43 + b 43 + c 43 + d 43 + f 43 + 11 44 + 12 44 + 13 44 + 16 44 + 1a 44 + 1b 44 + 1c 44 + 1d 44 + 1f 44 + 21 45 + 22 45 + 23 45 + 26 45 + 2a 45 + 2b 45 + 2c 45 + 2d 45 + 2f 45 + 31 46 + 32 46 + 33 46 + 36 46 + 3a 46 + 3b 46 + 3c 46 + 3d 46 + 3f 46 + 41 47 + 42 47 + 43 47 + 46 47 + 4a 47 + 4b 47 + 4c 47 + 4d 47 + 4f 47 + 51 48 + 52 48 + 53 48 + 56 48 + 5a 48 + 5b 48 + 5c 48 + 5d 48 + 5f 48 + 61 49 + 62 49 + 63 49 + 66 49 + 6a 49 + 6b 49 + 6c 49 + 6d 49 + 6f 49 + 71 50 + 72 50 + 73 50 + 76 50 + 7a 50 + 7b 50 + 7c 50 + 7d 50 + 7f 50 + 81 51 + 82 51 + 83 51 + 86 51 + 8a 51 + 8b 51 + 8c 51 + 8d 51 + 8f 51 + 90 52 + 91 52 + 92 52 + 93 52 + 96 52 + 9a 52 + 9b 52 + 9c 52 + 9d 52 + 9e 52 + a1 52 } method 'testArrayOp (II[II)I' { - 1 60 - 2 60 - 3 60 - 4 60 - 6 60 - 8 60 - a 61 - b 61 - c 61 - d 61 - f 61 - 11 61 - 13 62 - 14 62 - 15 62 - 16 62 - 18 62 - 1a 62 - 1c 63 - 1d 63 - 1e 63 - 1f 63 - 21 63 - 23 63 - 25 64 - 26 64 - 27 64 - 28 64 - 2a 64 - 2c 64 - 2e 65 - 2f 65 - 30 65 - 31 65 - 33 65 - 35 65 - 37 66 - 38 66 - 39 66 - 3a 66 - 3c 66 - 3e 66 - 40 67 - 41 67 - 42 67 - 43 67 - 45 67 - 47 67 - 49 68 - 4a 68 - 4b 68 - 4c 68 - 4e 68 - 50 68 - 52 69 - 53 69 - 54 69 - 55 69 - 57 69 - 59 69 - 5a 70 - 5b 70 + 1 56 + 2 56 + 3 56 + 4 56 + 6 56 + 8 56 + a 57 + b 57 + c 57 + d 57 + f 57 + 11 57 + 13 58 + 14 58 + 15 58 + 16 58 + 18 58 + 1a 58 + 1c 59 + 1d 59 + 1e 59 + 1f 59 + 21 59 + 23 59 + 25 60 + 26 60 + 27 60 + 28 60 + 2a 60 + 2c 60 + 2e 61 + 2f 61 + 30 61 + 31 61 + 33 61 + 35 61 + 37 62 + 38 62 + 39 62 + 3a 62 + 3c 62 + 3e 62 + 40 63 + 41 63 + 42 63 + 43 63 + 45 63 + 47 63 + 49 64 + 4a 64 + 4b 64 + 4c 64 + 4e 64 + 50 64 + 51 65 + 52 65 + 53 65 + 54 65 + 55 65 + 57 65 + 58 65 + 5b 65 } } @@ -390,48 +385,48 @@ Lines mapping: 12 <-> 12 13 <-> 13 14 <-> 14 -16 <-> 15 -20 <-> 19 -21 <-> 20 -22 <-> 21 -23 <-> 22 -24 <-> 23 -25 <-> 24 -26 <-> 25 -27 <-> 26 -28 <-> 27 -29 <-> 28 -31 <-> 29 -35 <-> 33 -36 <-> 34 -37 <-> 35 -38 <-> 36 -39 <-> 37 -40 <-> 38 -41 <-> 39 -42 <-> 40 -43 <-> 41 -44 <-> 42 -46 <-> 43 -50 <-> 47 -51 <-> 48 -52 <-> 49 -53 <-> 50 -54 <-> 51 -55 <-> 52 -56 <-> 53 -57 <-> 54 -58 <-> 55 -59 <-> 56 -61 <-> 57 -65 <-> 61 -66 <-> 62 -67 <-> 63 -68 <-> 64 -69 <-> 65 -70 <-> 66 -71 <-> 67 -72 <-> 68 -73 <-> 69 -74 <-> 70 -76 <-> 71 +16 <-> 14 +20 <-> 18 +21 <-> 19 +22 <-> 20 +23 <-> 21 +24 <-> 22 +25 <-> 23 +26 <-> 24 +27 <-> 25 +28 <-> 26 +29 <-> 27 +31 <-> 27 +35 <-> 31 +36 <-> 32 +37 <-> 33 +38 <-> 34 +39 <-> 35 +40 <-> 36 +41 <-> 37 +42 <-> 38 +43 <-> 39 +44 <-> 40 +46 <-> 40 +50 <-> 44 +51 <-> 45 +52 <-> 46 +53 <-> 47 +54 <-> 48 +55 <-> 49 +56 <-> 50 +57 <-> 51 +58 <-> 52 +59 <-> 53 +61 <-> 53 +65 <-> 57 +66 <-> 58 +67 <-> 59 +68 <-> 60 +69 <-> 61 +70 <-> 62 +71 <-> 63 +72 <-> 64 +73 <-> 65 +74 <-> 66 +76 <-> 66 diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestCompoundAssignment.java b/plugins/java-decompiler/engine/testData/src/pkg/TestCompoundAssignment.java new file mode 100644 index 000000000000..e356c63affc4 --- /dev/null +++ b/plugins/java-decompiler/engine/testData/src/pkg/TestCompoundAssignment.java @@ -0,0 +1,78 @@ +package pkg; + +public class TestCompoundAssignment { + public int testSimple(int i, int j) { + i += j; + i -= j; + i *= j; + i /= j; + i &= j; + i |= j; + i ^= j; + i >>= j; + i <<= j; + i >>>= j; + + return i; + } + + public int testComplex(int i, int j, int k) { + i += j + k; + i -= j + k; + i *= j + k; + i /= j + k; + i &= j + k; + i |= j + k; + i ^= j + k; + i >>= j + k; + i <<= j + k; + i >>>= j + k; + + return i; + } + + public int testComplexParens(int i, int j, int k, int m) { + i += (j + k) * m; + i -= (j + k) * m; + i *= (j + k) * m; + i /= (j + k) * m; + i &= (j + k) * m; + i |= (j + k) * m; + i ^= (j + k) * m; + i >>= (j + k) * m; + i <<= (j + k) * m; + i >>>= (j + k) * m; + + return i; + } + + public int testComplexTernary(int i, int j, int k, int m, boolean b) { + i += b ? j : k * m; + i -= b ? j : k * m; + i *= b ? j : k * m; + i /= b ? j : k * m; + i &= b ? j : k * m; + i |= b ? j : k * m; + i ^= b ? j : k * m; + i >>= b ? j : k * m; + i <<= b ? j : k * m; + i >>>= b ? j : k * m; + + return i; + } + + public int testArrayOp(int i, int j, int[] a, int b) { + i += a[b] = j; + i -= a[b] = j; + i *= a[b] = j; + i /= a[b] = j; + i &= a[b] = j; + i |= a[b] = j; + i ^= a[b] = j; + i >>= a[b] = j; + i <<= a[b] = j; + i >>>= a[b] = j; + + return i; + } +}