From 564c483b8986dae9bbda1fd22cf8ae93d66780e1 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:08:32 -0400 Subject: [PATCH 01/17] Start 1.11.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 07d2958634..251d1ac9f0 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ allprojects { group = 'org.vineflower' archivesBaseName = 'vineflower' -version = '1.10.0' +version = '1.11.0' def ENV = System.getenv() version = version + (ENV.GITHUB_ACTIONS ? "" : "+local") From 765a698d534a53cb32a570d91324e690fcb23484 Mon Sep 17 00:00:00 2001 From: coehlrich Date: Sat, 6 Apr 2024 03:46:14 +1300 Subject: [PATCH 02/17] Fix switches on enums with a switch map along with null cases for java 21 (#355) * Fix switches with enums for enums in other classes and null cases * Remove println --- .../java/decompiler/code/BytecodeVersion.java | 6 +- .../SwitchPatternMatchProcessor.java | 2 +- .../decompiler/exps/SwitchExprent.java | 3 +- .../java/decompiler/SingleClassesTest.java | 2 +- testData/results/pkg/TestSwitchOnEnumJ21.dec | 273 ++++++++++++------ testData/src/java21/ext/TestEnum2.java | 7 + .../src/java21/pkg/TestSwitchOnEnumJ21.java | 35 +++ 7 files changed, 234 insertions(+), 94 deletions(-) create mode 100644 testData/src/java21/ext/TestEnum2.java diff --git a/src/org/jetbrains/java/decompiler/code/BytecodeVersion.java b/src/org/jetbrains/java/decompiler/code/BytecodeVersion.java index ba919b14c3..e0ba94718c 100644 --- a/src/org/jetbrains/java/decompiler/code/BytecodeVersion.java +++ b/src/org/jetbrains/java/decompiler/code/BytecodeVersion.java @@ -42,7 +42,7 @@ public boolean hasSwitchExpressions() { } public boolean hasSwitchPatternMatch() { - return previewFrom(MAJOR_17); + return previewReleased(MAJOR_17, MAJOR_21); } public boolean hasSealedClasses() { @@ -112,4 +112,8 @@ public int hashCode() { public static final int MAJOR_15 = 59; public static final int MAJOR_16 = 60; public static final int MAJOR_17 = 61; + public static final int MAJOR_18 = 62; + public static final int MAJOR_19 = 63; + public static final int MAJOR_20 = 64; + public static final int MAJOR_21 = 65; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternMatchProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternMatchProcessor.java index 35acab384a..3f526e824f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternMatchProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternMatchProcessor.java @@ -398,6 +398,6 @@ private static boolean isSwitchPatternMatch(SwitchHeadExprent head) { } public static boolean hasPatternMatch(RootStatement root) { - return root.mt.getBytecodeVersion().hasSwitchPatternMatch() && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_PREVIEW); + return root.mt.getBytecodeVersion().hasSwitchPatternMatch() && DecompilerContext.getOption(IFernflowerPreferences.PATTERN_MATCHING); } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java index aca49427da..b3a051cf20 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java @@ -215,7 +215,8 @@ private static boolean isSyntheticThrowEdge(StatEdge edge){ Exprent targetExpr = targetExprs.get(0); return targetExpr instanceof ExitExprent && ((ExitExprent) targetExpr).getExitType() == ExitExprent.Type.THROW - && ((ExitExprent) targetExpr).getValue().getExprType().value.equals("java/lang/IncompatibleClassChangeError"); + && (((ExitExprent) targetExpr).getValue().getExprType().value.equals("java/lang/IncompatibleClassChangeError") + || ((ExitExprent) targetExpr).getValue().getExprType().value.equals("java/lang/MatchException")); } return false; } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index a81d6cec22..8c89946ed1 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -693,7 +693,7 @@ private void registerDefault() { register(JAVA_16, "TestMissingLambdaBody"); register(JAVA_21_PREVIEW, "TestUnnamedVar1"); register(JAVA_8, "TestDanglingBoxingCall"); - register(JAVA_21, "TestSwitchOnEnumJ21"); + register(JAVA_21, "TestSwitchOnEnumJ21", "ext/TestEnum2"); register(JAVA_21, "TestInnerClassesJ21"); register(JAVA_8, "TestInnerClassesJ8"); } diff --git a/testData/results/pkg/TestSwitchOnEnumJ21.dec b/testData/results/pkg/TestSwitchOnEnumJ21.dec index cff9f9f8cf..a496dc6309 100644 --- a/testData/results/pkg/TestSwitchOnEnumJ21.dec +++ b/testData/results/pkg/TestSwitchOnEnumJ21.dec @@ -1,43 +1,78 @@ package pkg; +import ext.TestEnum2; + public class TestSwitchOnEnumJ21 { public int test1(TestSwitchOnEnumJ21.TestEnum a) { - return switch (a) {// 5 - case A -> 1;// 6 - case B -> 2;// 7 - case C -> 3;// 8 + return switch (a) {// 7 + case A -> 1;// 8 + case B -> 2;// 9 + case C -> 3;// 10 + }; + } + + public int test2(TestEnum2 a) { + return switch (a) {// 15 + case A -> 1;// 16 + case B -> 2;// 17 + case C -> 3;// 18 + }; + } + + public int test3(TestSwitchOnEnumJ21.TestEnum a) { + return switch (a) {// 23 + case null -> 4;// 27 + case A -> 1;// 24 + case B -> 2;// 25 + case C -> 3;// 26 + }; + } + + public int test4(TestEnum2 a) { + return switch (a) {// 32 + case null -> 4;// 36 + case A -> 1;// 33 + case B -> 2;// 34 + case C -> 3;// 35 }; } public int testDefault(TestSwitchOnEnumJ21.TestEnum a) { - return switch (a) {// 13 - case A -> 1;// 14 - default -> 5;// 15 + return switch (a) {// 41 + case A -> 1;// 42 + default -> 5;// 43 + }; + } + + public int testDefault2(TestEnum2 a) { + return switch (a) {// 48 + case A -> 1;// 49 + default -> 5;// 50 }; } public void testStatement(TestSwitchOnEnumJ21.TestEnum a) { - switch (a) {// 20 + switch (a) {// 55 case A: - System.out.println("A");// 22 - break;// 23 + System.out.println("A");// 57 + break;// 58 case B: - System.out.println("B");// 25 - break;// 26 + System.out.println("B");// 60 + break;// 61 case C: - System.out.println("C");// 28 + System.out.println("C");// 63 } - }// 30 + }// 65 public void testStatementDefault(TestSwitchOnEnumJ21.TestEnum a) { - switch (a) {// 33 + switch (a) {// 68 case A: - System.out.println("A");// 35 - break;// 36 + System.out.println("A");// 70 + break;// 71 default: - System.out.println("C");// 38 + System.out.println("C");// 73 } - }// 40 + }// 75 static enum TestEnum { A, @@ -48,91 +83,149 @@ public class TestSwitchOnEnumJ21 { class 'pkg/TestSwitchOnEnumJ21' { method 'test1 (Lpkg/TestSwitchOnEnumJ21$TestEnum;)I' { - 0 4 - 4 4 - 2a 5 - 2e 6 - 32 7 - 33 4 + 0 6 + 4 6 + 2a 7 + 2e 8 + 32 9 + 33 6 + } + + method 'test2 (Lext/TestEnum2;)I' { + 3 14 + 7 14 + 8 14 + 2e 15 + 32 16 + 36 17 + 37 14 + } + + method 'test3 (Lpkg/TestSwitchOnEnumJ21$TestEnum;)I' { + 0 22 + 4 22 + b 22 + 32 24 + 36 25 + 3a 26 + 3e 23 + 3f 22 + } + + method 'test4 (Lext/TestEnum2;)I' { + 0 31 + 4 31 + b 31 + 32 33 + 36 34 + 3a 35 + 3e 32 + 3f 31 } method 'testDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)I' { - 0 12 - 4 12 - 18 13 - 1c 14 - 1d 12 + 0 40 + 4 40 + 18 41 + 1c 42 + 1d 40 + } + + method 'testDefault2 (Lext/TestEnum2;)I' { + 3 47 + 7 47 + 8 47 + 1c 48 + 20 49 + 21 47 } method 'testStatement (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { - 0 19 - 4 19 - 20 21 - 21 21 - 22 21 - 23 21 - 24 21 - 25 21 - 26 21 - 27 21 - 28 22 - 2b 24 - 2c 24 - 2d 24 - 2e 24 - 2f 24 - 30 24 - 31 24 - 32 24 - 33 25 - 36 27 - 37 27 - 38 27 - 39 27 - 3a 27 - 3b 27 - 3e 29 + 0 54 + 4 54 + 20 56 + 21 56 + 22 56 + 23 56 + 24 56 + 25 56 + 26 56 + 27 56 + 28 57 + 2b 59 + 2c 59 + 2d 59 + 2e 59 + 2f 59 + 30 59 + 31 59 + 32 59 + 33 60 + 36 62 + 37 62 + 38 62 + 39 62 + 3a 62 + 3b 62 + 3e 64 } method 'testStatementDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { - 0 32 - 4 32 - 18 34 - 19 34 - 1a 34 - 1b 34 - 1c 34 - 1d 34 - 1e 34 - 1f 34 - 20 35 - 23 37 - 24 37 - 25 37 - 26 37 - 27 37 - 28 37 - 2b 39 + 0 67 + 4 67 + 18 69 + 19 69 + 1a 69 + 1b 69 + 1c 69 + 1d 69 + 1e 69 + 1f 69 + 20 70 + 23 72 + 24 72 + 25 72 + 26 72 + 27 72 + 28 72 + 2b 74 } } Lines mapping: -5 <-> 5 -6 <-> 6 7 <-> 7 8 <-> 8 -13 <-> 13 -14 <-> 14 +9 <-> 9 +10 <-> 10 15 <-> 15 -20 <-> 20 -22 <-> 22 +16 <-> 16 +17 <-> 17 +18 <-> 18 23 <-> 23 -25 <-> 25 -26 <-> 26 -28 <-> 28 -30 <-> 30 -33 <-> 33 -35 <-> 35 -36 <-> 36 -38 <-> 38 -40 <-> 40 \ No newline at end of file +24 <-> 25 +25 <-> 26 +26 <-> 27 +27 <-> 24 +32 <-> 32 +33 <-> 34 +34 <-> 35 +35 <-> 36 +36 <-> 33 +41 <-> 41 +42 <-> 42 +43 <-> 43 +48 <-> 48 +49 <-> 49 +50 <-> 50 +55 <-> 55 +57 <-> 57 +58 <-> 58 +60 <-> 60 +61 <-> 61 +63 <-> 63 +65 <-> 65 +68 <-> 68 +70 <-> 70 +71 <-> 71 +73 <-> 73 +75 <-> 75 diff --git a/testData/src/java21/ext/TestEnum2.java b/testData/src/java21/ext/TestEnum2.java new file mode 100644 index 0000000000..bcc0edddad --- /dev/null +++ b/testData/src/java21/ext/TestEnum2.java @@ -0,0 +1,7 @@ +package ext; + +public enum TestEnum2 { + A, + B, + C +} diff --git a/testData/src/java21/pkg/TestSwitchOnEnumJ21.java b/testData/src/java21/pkg/TestSwitchOnEnumJ21.java index f3fc6a301b..1aff25e24e 100644 --- a/testData/src/java21/pkg/TestSwitchOnEnumJ21.java +++ b/testData/src/java21/pkg/TestSwitchOnEnumJ21.java @@ -1,5 +1,7 @@ package pkg; +import ext.TestEnum2; + public class TestSwitchOnEnumJ21 { public int test1(TestEnum a) { return switch (a) { @@ -8,6 +10,32 @@ public int test1(TestEnum a) { case C -> 3; }; } + + public int test2(TestEnum2 a) { + return switch (a) { + case A -> 1; + case B -> 2; + case C -> 3; + }; + } + + public int test3(TestEnum a) { + return switch (a) { + case A -> 1; + case B -> 2; + case C -> 3; + case null -> 4; + }; + } + + public int test4(TestEnum2 a) { + return switch (a) { + case A -> 1; + case B -> 2; + case C -> 3; + case null -> 4; + }; + } public int testDefault(TestEnum a) { return switch (a) { @@ -15,6 +43,13 @@ public int testDefault(TestEnum a) { default -> 5; }; } + + public int testDefault2(TestEnum2 a) { + return switch (a) { + case A -> 1; + default -> 5; + }; + } public void testStatement(TestEnum a) { switch (a) { From 451ab666b1f61ac19d21dafab2fc43647834cec6 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:15:12 -0400 Subject: [PATCH 03/17] Bump to Java 17 --- .github/workflows/build.yml | 2 +- build.gradle | 6 +- testData/results/pkg/TestCaseClasses.dec | 806 +++++++++----------- testData/results/pkg/TestKotlinEnumWhen.dec | 463 ++++++----- 4 files changed, 602 insertions(+), 675 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12233a8ebb..c34e3df451 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ jobs: build: strategy: matrix: - java: [11, 17, 19] + java: [17, 21] runs-on: ubuntu-22.04 container: image: eclipse-temurin:${{ matrix.java }} diff --git a/build.gradle b/build.gradle index 251d1ac9f0..319d94191b 100644 --- a/build.gradle +++ b/build.gradle @@ -25,12 +25,12 @@ allprojects { group = 'org.vineflower' compileJava { - sourceCompatibility = '11' - targetCompatibility = '11' + sourceCompatibility = '17' + targetCompatibility = '17' } java.toolchain { - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(17) } repositories { diff --git a/testData/results/pkg/TestCaseClasses.dec b/testData/results/pkg/TestCaseClasses.dec index 5615f4586a..dd969f55a3 100644 --- a/testData/results/pkg/TestCaseClasses.dec +++ b/testData/results/pkg/TestCaseClasses.dec @@ -39,21 +39,7 @@ public class Option1 implements TestCaseClasses, Product, Serializable { } public boolean equals(final Object x$0) { - if (this != x$0) { - boolean var10000; - if (x$0 instanceof Option1) { - Option1 var3 = (Option1)x$0; - var10000 = this.value() == var3.value() && var3.canEqual(this); - } else { - var10000 = false; - } - - if (!var10000) { - return false; - } - } - - return true; + return this == x$0 || (x$0 instanceof Option1 var3 ? this.value() == var3.value() && var3.canEqual(this) : false); } public String toString() { @@ -185,151 +171,144 @@ class 'pkg/Option1' { 0 35 1 35 2 35 - 5 37 - 7 37 - 8 37 - 9 37 - a 37 - b 37 - e 38 - f 38 - 10 38 - 11 38 - 12 38 - 13 39 - 14 39 - 15 39 - 16 39 - 17 39 - 18 39 - 19 39 - 1a 39 - 1b 39 - 1e 39 - 1f 39 - 20 39 - 21 39 - 22 39 - 23 39 - 2e 41 - 32 44 - 35 49 - 39 45 - 3a 45 + 5 35 + 7 35 + 8 35 + 9 35 + a 35 + b 35 + 12 35 + 13 35 + 14 35 + 15 35 + 16 35 + 17 35 + 18 35 + 19 35 + 1a 35 + 1b 35 + 1e 35 + 1f 35 + 20 35 + 21 35 + 22 35 + 23 35 + 32 35 + 3a 35 } method 'toString ()Ljava/lang/String;' { - 0 53 - 1 53 - 2 53 - 3 53 - 4 53 - 5 53 - 6 53 - 7 53 + 0 39 + 1 39 + 2 39 + 3 39 + 4 39 + 5 39 + 6 39 + 7 39 } method 'canEqual (Ljava/lang/Object;)Z' { - 0 57 - 1 57 - 2 57 - 3 57 - 4 57 + 0 43 + 1 43 + 2 43 + 3 43 + 4 43 } method 'productArity ()I' { - 0 61 - 1 61 + 0 47 + 1 47 } method 'productPrefix ()Ljava/lang/String;' { - 0 65 - 1 65 - 2 65 + 0 51 + 1 51 + 2 51 } method 'productElement (I)Ljava/lang/Object;' { - 0 69 - 2 69 - 3 69 - 4 69 - 7 70 - 8 70 - 9 70 - a 70 - b 70 - c 70 - d 70 - e 70 - f 70 - 10 70 - 11 70 - 12 70 - 13 70 - 14 70 - 15 70 - 16 70 - 17 70 - 18 70 - 19 70 - 1a 70 - 1b 70 - 1c 70 - 1d 70 - 1e 70 - 1f 70 + 0 55 + 2 55 + 3 55 + 4 55 + 7 56 + 8 56 + 9 56 + a 56 + b 56 + c 56 + d 56 + e 56 + f 56 + 10 56 + 11 56 + 12 56 + 13 56 + 14 56 + 15 56 + 16 56 + 17 56 + 18 56 + 19 56 + 1a 56 + 1b 56 + 1c 56 + 1d 56 + 1e 56 + 1f 56 } method 'productElementName (I)Ljava/lang/String;' { - 0 77 - 2 77 - 3 77 - 4 77 - 7 78 - 8 78 - 9 78 - e 80 - f 80 - 10 80 - 11 80 - 12 80 - 13 80 - 14 80 - 18 80 + 0 63 + 2 63 + 3 63 + 4 63 + 7 64 + 8 64 + 9 64 + e 66 + f 66 + 10 66 + 11 66 + 12 66 + 13 66 + 14 66 + 18 66 } method 'value ()I' { - 0 85 - 1 85 - 2 85 - 3 85 - 4 85 + 0 71 + 1 71 + 2 71 + 3 71 + 4 71 } method 'copy (I)Lpkg/Option1;' { - 4 89 - 8 89 + 4 75 + 8 75 } method 'copy$default$1 ()I' { - 0 93 - 1 93 - 2 93 - 3 93 - 4 93 + 0 79 + 1 79 + 2 79 + 3 79 + 4 79 } method '_1 ()I' { - 0 97 - 1 97 - 2 97 - 3 97 - 4 97 + 0 83 + 1 83 + 2 83 + 3 83 + 4 83 } } Lines mapping: -5 <-> 98 +5 <-> 84 // Decompiled companion from pkg/Option1$ package pkg; @@ -453,21 +432,7 @@ public class Option2 implements TestCaseClasses, Product, Serializable { } public boolean equals(final Object x$0) { - if (this != x$0) { - boolean var10000; - if (x$0 instanceof Option2) { - Option2 var3 = (Option2)x$0; - var10000 = this.x() == var3.x() && this.y() == var3.y() && this.z() == var3.z() && var3.canEqual(this); - } else { - var10000 = false; - } - - if (!var10000) { - return false; - } - } - - return true; + return this == x$0 || (x$0 instanceof Option2 var3 ? this.x() == var3.x() && this.y() == var3.y() && this.z() == var3.z() && var3.canEqual(this) : false); } public String toString() { @@ -487,22 +452,12 @@ public class Option2 implements TestCaseClasses, Product, Serializable { } public Object productElement(final int n) { - double var10000; - switch (n) { - case 0: - var10000 = this._1(); - break; - case 1: - var10000 = this._2(); - break; - case 2: - var10000 = this._3(); - break; - default: - throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(n).toString()); - } - - return BoxesRunTime.boxToDouble(var10000); + return BoxesRunTime.boxToDouble(switch (n) { + case 0 -> this._1(); + case 1 -> this._2(); + case 2 -> this._3(); + default -> throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(n).toString()); + }); } public String productElementName(final int n) { @@ -680,229 +635,219 @@ class 'pkg/Option2' { 0 41 1 41 2 41 - 5 43 - 7 43 - 8 43 - 9 43 - a 43 - b 43 - e 44 - f 44 - 10 44 - 11 44 - 12 44 - 13 45 - 14 45 - 15 45 - 16 45 - 17 45 - 18 45 - 19 45 - 1a 45 - 1b 45 - 1c 45 - 1f 45 - 20 45 - 21 45 - 22 45 - 23 45 - 24 45 - 25 45 - 26 45 - 27 45 - 28 45 - 2b 45 - 2c 45 - 2d 45 - 2e 45 - 2f 45 - 30 45 - 31 45 - 32 45 - 33 45 - 34 45 - 37 45 - 38 45 - 39 45 - 3a 45 - 3b 45 - 3c 45 - 47 47 - 4b 50 - 4e 55 - 52 51 - 53 51 + 5 41 + 7 41 + 8 41 + 9 41 + a 41 + b 41 + 12 41 + 13 41 + 14 41 + 15 41 + 16 41 + 17 41 + 18 41 + 19 41 + 1a 41 + 1b 41 + 1c 41 + 1f 41 + 20 41 + 21 41 + 22 41 + 23 41 + 24 41 + 25 41 + 26 41 + 27 41 + 28 41 + 2b 41 + 2c 41 + 2d 41 + 2e 41 + 2f 41 + 30 41 + 31 41 + 32 41 + 33 41 + 34 41 + 37 41 + 38 41 + 39 41 + 3a 41 + 3b 41 + 3c 41 + 4b 41 + 53 41 } method 'toString ()Ljava/lang/String;' { - 0 59 - 1 59 - 2 59 - 3 59 - 4 59 - 5 59 - 6 59 - 7 59 + 0 45 + 1 45 + 2 45 + 3 45 + 4 45 + 5 45 + 6 45 + 7 45 } method 'canEqual (Ljava/lang/Object;)Z' { - 0 63 - 1 63 - 2 63 - 3 63 - 4 63 + 0 49 + 1 49 + 2 49 + 3 49 + 4 49 } method 'productArity ()I' { - 0 67 - 1 67 + 0 53 + 1 53 } method 'productPrefix ()Ljava/lang/String;' { - 0 71 - 1 71 - 2 71 + 0 57 + 1 57 + 2 57 } method 'productElement (I)Ljava/lang/Object;' { - 0 76 - 2 76 - 3 76 - 1c 78 - 1d 78 - 1e 78 - 1f 78 - 20 79 - 23 81 - 24 81 - 25 81 - 26 81 - 27 82 - 2a 84 - 2b 84 - 2c 84 - 2d 84 - 2e 85 - 35 87 - 36 87 - 37 87 - 38 87 - 39 87 - 3a 87 - 3b 87 - 3f 87 - 41 90 - 42 90 - 43 90 - 44 90 + 0 61 + 2 61 + 3 61 + 1c 62 + 1d 62 + 1e 62 + 1f 62 + 23 63 + 24 63 + 25 63 + 26 63 + 2a 64 + 2b 64 + 2c 64 + 2d 64 + 35 65 + 36 65 + 37 65 + 38 65 + 39 65 + 3a 65 + 3b 65 + 3f 65 + 41 61 + 42 61 + 43 61 + 44 61 } method 'productElementName (I)Ljava/lang/String;' { - 0 94 - 2 94 - 3 94 - 1c 96 - 1d 96 - 1e 96 - 1f 98 - 20 98 - 21 98 - 22 100 - 23 100 - 24 100 - 29 102 - 2a 102 - 2b 102 - 2c 102 - 2d 102 - 2e 102 - 2f 102 - 33 102 + 0 70 + 2 70 + 3 70 + 1c 72 + 1d 72 + 1e 72 + 1f 74 + 20 74 + 21 74 + 22 76 + 23 76 + 24 76 + 29 78 + 2a 78 + 2b 78 + 2c 78 + 2d 78 + 2e 78 + 2f 78 + 33 78 } method 'x ()D' { - 0 107 - 1 107 - 2 107 - 3 107 - 4 107 + 0 83 + 1 83 + 2 83 + 3 83 + 4 83 } method 'y ()D' { - 0 111 - 1 111 - 2 111 - 3 111 - 4 111 + 0 87 + 1 87 + 2 87 + 3 87 + 4 87 } method 'z ()D' { - 0 115 - 1 115 - 2 115 - 3 115 - 4 115 + 0 91 + 1 91 + 2 91 + 3 91 + 4 91 } method 'copy (DDD)Lpkg/Option2;' { - 4 119 - 5 119 - 6 119 - 7 119 - b 119 + 4 95 + 5 95 + 6 95 + 7 95 + b 95 } method 'copy$default$1 ()D' { - 0 123 - 1 123 - 2 123 - 3 123 - 4 123 + 0 99 + 1 99 + 2 99 + 3 99 + 4 99 } method 'copy$default$2 ()D' { - 0 127 - 1 127 - 2 127 - 3 127 - 4 127 + 0 103 + 1 103 + 2 103 + 3 103 + 4 103 } method 'copy$default$3 ()D' { - 0 131 - 1 131 - 2 131 - 3 131 - 4 131 + 0 107 + 1 107 + 2 107 + 3 107 + 4 107 } method '_1 ()D' { - 0 135 - 1 135 - 2 135 - 3 135 - 4 135 + 0 111 + 1 111 + 2 111 + 3 111 + 4 111 } method '_2 ()D' { - 0 139 - 1 139 - 2 139 - 3 139 - 4 139 + 0 115 + 1 115 + 2 115 + 3 115 + 4 115 } method '_3 ()D' { - 0 143 - 1 143 - 2 143 - 3 143 - 4 143 + 0 119 + 1 119 + 2 119 + 3 119 + 4 119 } } Lines mapping: -6 <-> 144 +6 <-> 120 // Decompiled companion from pkg/Option2$ package pkg; @@ -1046,10 +991,9 @@ public class Option3 implements TestCaseClasses, Product, Serializable { public boolean equals(final Object x$0) { if (this != x$0) { boolean var5; - if (!(x$0 instanceof Option3)) { + if (!(x$0 instanceof Option3 var3)) { var5 = false; } else { - Option3 var3 = (Option3)x$0; List var10000 = this.value(); List var4 = var3.value(); var5 = (var10000 == null ? var4 == null : var10000.equals(var4)) && var3.canEqual(this); @@ -1176,144 +1120,140 @@ class 'pkg/Option3' { 9 34 a 34 b 34 - e 37 - f 37 - 10 37 - 11 37 - 12 37 - 13 38 - 14 38 - 15 38 - 16 38 - 17 39 - 18 39 - 19 39 - 1a 39 - 1b 39 - 1c 39 - 1e 40 - 22 40 - 23 40 - 24 40 - 2a 40 - 2b 40 - 2c 40 - 2d 40 - 2e 40 - 2f 40 - 32 40 - 33 40 - 34 40 - 35 40 - 36 40 - 37 40 + 12 34 + 13 37 + 14 37 + 15 37 + 16 37 + 17 38 + 18 38 + 19 38 + 1a 38 + 1b 38 + 1c 38 + 1e 39 + 22 39 + 23 39 + 24 39 + 2a 39 + 2b 39 + 2c 39 + 2d 39 + 2e 39 + 2f 39 + 32 39 + 33 39 + 34 39 + 35 39 + 36 39 + 37 39 42 35 - 46 43 - 49 48 - 4d 44 - 4e 44 + 46 42 + 49 47 + 4d 43 + 4e 43 } method 'toString ()Ljava/lang/String;' { - 0 52 - 1 52 - 2 52 - 3 52 - 4 52 - 5 52 - 6 52 - 7 52 + 0 51 + 1 51 + 2 51 + 3 51 + 4 51 + 5 51 + 6 51 + 7 51 } method 'canEqual (Ljava/lang/Object;)Z' { - 0 56 - 1 56 - 2 56 - 3 56 - 4 56 + 0 55 + 1 55 + 2 55 + 3 55 + 4 55 } method 'productArity ()I' { - 0 60 - 1 60 + 0 59 + 1 59 } method 'productPrefix ()Ljava/lang/String;' { - 0 64 - 1 64 - 2 64 + 0 63 + 1 63 + 2 63 } method 'productElement (I)Ljava/lang/Object;' { - 0 68 - 2 68 - 3 68 - 4 68 - 7 69 - 8 69 - 9 69 - a 69 - b 69 - 10 71 - 11 71 - 12 71 - 13 71 - 14 71 - 15 71 - 16 71 - 1a 71 + 0 67 + 2 67 + 3 67 + 4 67 + 7 68 + 8 68 + 9 68 + a 68 + b 68 + 10 70 + 11 70 + 12 70 + 13 70 + 14 70 + 15 70 + 16 70 + 1a 70 } method 'productElementName (I)Ljava/lang/String;' { - 0 76 - 2 76 - 3 76 - 4 76 - 7 77 - 8 77 - 9 77 - e 79 - f 79 - 10 79 - 11 79 - 12 79 - 13 79 - 14 79 - 18 79 + 0 75 + 2 75 + 3 75 + 4 75 + 7 76 + 8 76 + 9 76 + e 78 + f 78 + 10 78 + 11 78 + 12 78 + 13 78 + 14 78 + 18 78 } method 'value ()Lscala/collection/immutable/List;' { - 0 84 - 1 84 - 2 84 - 3 84 - 4 84 + 0 83 + 1 83 + 2 83 + 3 83 + 4 83 } method 'copy (Lscala/collection/immutable/List;)Lpkg/Option3;' { - 4 88 - 8 88 + 4 87 + 8 87 } method 'copy$default$1 ()Lscala/collection/immutable/List;' { - 0 92 - 1 92 - 2 92 - 3 92 - 4 92 + 0 91 + 1 91 + 2 91 + 3 91 + 4 91 } method '_1 ()Lscala/collection/immutable/List;' { - 0 96 - 1 96 - 2 96 - 3 96 - 4 96 + 0 95 + 1 95 + 2 95 + 3 95 + 4 95 } } Lines mapping: -7 <-> 97 +7 <-> 96 // Decompiled companion from pkg/Option3$ package pkg; diff --git a/testData/results/pkg/TestKotlinEnumWhen.dec b/testData/results/pkg/TestKotlinEnumWhen.dec index f66da1dbcc..297fd24016 100644 --- a/testData/results/pkg/TestKotlinEnumWhen.dec +++ b/testData/results/pkg/TestKotlinEnumWhen.dec @@ -30,22 +30,12 @@ public enum TestKotlinEnumWhen { }// 12 public final void testExpression() { - String var10000; - switch (this) {// 16 - case FIRST: - var10000 = "first!";// 17 - break; - case SECOND: - var10000 = "second!";// 18 - break; - case THIRD: - var10000 = "third!";// 19 - break; - default: - throw new NoWhenBranchMatchedException(); - } - - String var1 = var10000; + String var1 = switch (this) {// 16 + case FIRST -> "first!";// 17 + case SECOND -> "second!";// 18 + case THIRD -> "third!";// 19 + default -> throw new NoWhenBranchMatchedException(); + }; System.out.println(var1);// 15 }// 22 @@ -154,215 +144,212 @@ class 'pkg/TestKotlinEnumWhen' { } method 'testExpression ()V' { - 0 33 - 8 33 - 9 33 - 24 35 - 25 35 - 26 36 - 29 38 - 2a 38 - 2b 39 - 2e 41 - 2f 41 - 30 42 - 3a 44 - 3b 47 + 0 32 + 8 32 + 9 32 + 24 33 + 25 33 + 29 34 + 2a 34 + 2e 35 + 2f 35 + 3a 36 + 3b 32 + 3c 38 + 3d 38 + 3e 38 + 3f 38 + 40 38 + 41 38 + 42 38 + 43 39 + } + + method 'testAnotherEnum ()V' { + 0 42 + 1 42 + 2 42 + 3 42 + 7 43 + b 43 + c 43 + 28 45 + 29 45 + 2a 45 + 2b 45 + 2c 45 + 2d 45 + 2e 45 + 2f 45 + 30 45 + 32 45 + 33 45 + 34 45 + 35 46 + 38 48 + 39 48 + 3a 48 + 3b 48 3c 48 3d 48 3e 48 3f 48 40 48 - 41 48 42 48 - 43 49 - } - - method 'testAnotherEnum ()V' { - 0 52 - 1 52 - 2 52 - 3 52 - 7 53 - b 53 - c 53 - 28 55 - 29 55 - 2a 55 - 2b 55 - 2c 55 - 2d 55 - 2e 55 - 2f 55 - 30 55 - 32 55 - 33 55 - 34 55 - 35 56 - 38 58 - 39 58 - 3a 58 - 3b 58 - 3c 58 - 3d 58 - 3e 58 - 3f 58 - 40 58 - 42 58 - 43 58 - 44 58 - 45 59 - 48 61 - 49 61 - 4a 61 - 4b 61 - 4c 61 - 4d 61 - 4e 61 - 4f 61 - 50 61 - 52 61 - 55 63 + 43 48 + 44 48 + 45 49 + 48 51 + 49 51 + 4a 51 + 4b 51 + 4c 51 + 4d 51 + 4e 51 + 4f 51 + 50 51 + 52 51 + 55 53 } method 'testConsecutive ()V' { - 0 66 - 8 66 - 9 66 - 24 68 - 25 68 - 26 68 - 27 68 - 28 68 - 2a 68 - 2b 68 - 2c 68 - 2d 69 - 30 71 - 31 71 - 32 71 - 33 71 - 34 71 - 36 71 - 37 71 - 38 71 - 39 72 - 3c 74 - 3d 74 - 3e 74 - 3f 74 - 40 74 - 42 74 - 45 77 - 4d 77 - 4e 77 - 68 79 - 69 79 - 6a 79 - 6b 79 - 6c 79 - 6e 79 - 6f 79 - 70 79 - 71 80 - 74 82 - 75 82 - 76 82 - 77 82 - 78 82 - 7a 82 - 7b 82 - 7c 82 - 7d 83 - 80 85 - 81 85 - 82 85 - 83 85 - 84 85 - 86 85 - 89 87 + 0 56 + 8 56 + 9 56 + 24 58 + 25 58 + 26 58 + 27 58 + 28 58 + 2a 58 + 2b 58 + 2c 58 + 2d 59 + 30 61 + 31 61 + 32 61 + 33 61 + 34 61 + 36 61 + 37 61 + 38 61 + 39 62 + 3c 64 + 3d 64 + 3e 64 + 3f 64 + 40 64 + 42 64 + 45 67 + 4d 67 + 4e 67 + 68 69 + 69 69 + 6a 69 + 6b 69 + 6c 69 + 6e 69 + 6f 69 + 70 69 + 71 70 + 74 72 + 75 72 + 76 72 + 77 72 + 78 72 + 7a 72 + 7b 72 + 7c 72 + 7d 73 + 80 75 + 81 75 + 82 75 + 83 75 + 84 75 + 86 75 + 89 77 } method 'testConsecutiveMixed ()V' { - 0 90 - 1 90 - 2 90 - 3 90 - 7 91 - b 91 - c 91 - 28 93 - 29 93 - 2a 93 - 2b 93 - 2c 93 - 2d 93 - 2e 93 - 2f 93 - 30 93 - 32 93 - 33 93 - 34 93 - 35 94 - 38 96 - 39 96 - 3a 96 - 3b 96 - 3c 96 - 3d 96 - 3e 96 - 3f 96 - 40 96 - 42 96 - 43 96 - 44 96 - 45 97 - 48 99 - 49 99 - 4a 99 - 4b 99 - 4c 99 - 4d 99 - 4e 99 - 4f 99 - 50 99 - 52 99 - 55 102 - 5d 102 - 5e 102 - 78 104 - 79 104 - 7a 104 - 7b 104 - 7c 104 - 7e 104 - 7f 104 - 80 104 - 81 105 - 84 107 - 85 107 - 86 107 - 87 107 - 88 107 - 8a 107 - 8b 107 - 8c 107 - 8d 108 - 90 110 - 91 110 - 92 110 - 93 110 - 94 110 - 96 110 - 99 112 + 0 80 + 1 80 + 2 80 + 3 80 + 7 81 + b 81 + c 81 + 28 83 + 29 83 + 2a 83 + 2b 83 + 2c 83 + 2d 83 + 2e 83 + 2f 83 + 30 83 + 32 83 + 33 83 + 34 83 + 35 84 + 38 86 + 39 86 + 3a 86 + 3b 86 + 3c 86 + 3d 86 + 3e 86 + 3f 86 + 40 86 + 42 86 + 43 86 + 44 86 + 45 87 + 48 89 + 49 89 + 4a 89 + 4b 89 + 4c 89 + 4d 89 + 4e 89 + 4f 89 + 50 89 + 52 89 + 55 92 + 5d 92 + 5e 92 + 78 94 + 79 94 + 7a 94 + 7b 94 + 7c 94 + 7e 94 + 7f 94 + 80 94 + 81 95 + 84 97 + 85 97 + 86 97 + 87 97 + 88 97 + 8a 97 + 8b 97 + 8c 97 + 8d 98 + 90 100 + 91 100 + 92 100 + 93 100 + 94 100 + 96 100 + 99 102 } method 'testAnotherEnum$getLevel ()Lkotlin/DeprecationLevel;' { - 7 115 + 7 105 } method 'testConsecutiveMixed$getLevel-0 ()Lkotlin/DeprecationLevel;' { - 7 119 + 7 109 } } @@ -372,34 +359,34 @@ Lines mapping: 9 <-> 25 10 <-> 28 12 <-> 30 -15 <-> 49 -16 <-> 34 -17 <-> 36 -18 <-> 39 -19 <-> 42 -22 <-> 50 -26 <-> 116 -28 <-> 53 -29 <-> 56 -30 <-> 59 -31 <-> 62 -33 <-> 64 -36 <-> 67 -37 <-> 69 -38 <-> 72 -39 <-> 75 -42 <-> 78 -43 <-> 80 -44 <-> 83 -45 <-> 86 -47 <-> 88 -51 <-> 120 -53 <-> 91 -54 <-> 94 -55 <-> 97 -56 <-> 100 -59 <-> 103 -60 <-> 105 -61 <-> 108 -62 <-> 111 -64 <-> 113 +15 <-> 39 +16 <-> 33 +17 <-> 34 +18 <-> 35 +19 <-> 36 +22 <-> 40 +26 <-> 106 +28 <-> 43 +29 <-> 46 +30 <-> 49 +31 <-> 52 +33 <-> 54 +36 <-> 57 +37 <-> 59 +38 <-> 62 +39 <-> 65 +42 <-> 68 +43 <-> 70 +44 <-> 73 +45 <-> 76 +47 <-> 78 +51 <-> 110 +53 <-> 81 +54 <-> 84 +55 <-> 87 +56 <-> 90 +59 <-> 93 +60 <-> 95 +61 <-> 98 +62 <-> 101 +64 <-> 103 From 9541deff665e98194dbd30f201e2eb1270356efa Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:43:49 -0400 Subject: [PATCH 04/17] Make generic remapping more defensive --- .../decompiler/modules/decompiler/exps/InvocationExprent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index edea96b18b..9edebaae76 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -600,7 +600,7 @@ else if (paramType.type == CodeType.GENVAR && !paramType.equals(argtype) && argt } newRet = newRet.remap(genericsMap); - if (newRet == null) { + if (newRet == null && bounds.get(ret) != null && bounds.get(ret).size() > 0) { newRet = bounds.get(ret).get(0).remap(genericsMap); } From 9aa4ff31f47466b9c11d87e1788aabb2ae5ba6fd Mon Sep 17 00:00:00 2001 From: Kroppeb <7889478+Kroppeb@users.noreply.github.com> Date: Sat, 6 Apr 2024 00:44:59 +0200 Subject: [PATCH 05/17] Fix copying values in arrays (#357) --- .../modules/decompiler/ValidationHelper.java | 44 +++++++++ .../modules/decompiler/exps/NewExprent.java | 3 +- .../java/decompiler/SingleClassesTest.java | 1 + testData/results/pkg/TestSwitchInTry.dec | 96 +++++++++++++++++++ testData/src/java8/pkg/TestSwitchInTry.java | 26 +++++ 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 testData/results/pkg/TestSwitchInTry.dec create mode 100644 testData/src/java8/pkg/TestSwitchInTry.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ValidationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ValidationHelper.java index 7b61bac2b6..50a7cafa35 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/ValidationHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ValidationHelper.java @@ -357,6 +357,26 @@ public static void validateDGraph(DirectGraph graph, RootStatement root) { } } } + + // ensure all exprents are unique + Map, DirectNode> allExprents = new HashMap<>(); + + for (var node : graph.nodes) { + for (var exprent : node.exprents) { + for (var subExprent : exprent.getAllExprents(true, true)) { + ID key = new ID<>(subExprent); + if (allExprents.containsKey(key)) { + throw new IllegalStateException( + "Duplicated exprent: " + subExprent + " (Sub exprent of: " + exprent + ") in dgraph. " + + "Appears in both node " + node.id + " and node " + allExprents.get(key).id + "!" + ); + } else { + allExprents.put(key, node); + } + } + } + } + } catch (Throwable e) { DotExporter.errorToDotFile(graph, root.mt, "erroring_dgraph"); throw e; @@ -507,4 +527,28 @@ public static void validateVarVersionsGraph( throw new IllegalStateException("Highly cyclic varversions graph!"); } } + + + static private class ID { + private final T obj; + + + private ID(T obj) { + this.obj = obj; + } + + @Override + public int hashCode() { + return System.identityHashCode(this.obj); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ID id = (ID) o; + return obj == id.obj; + } + } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index f729806bea..44053eb734 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -9,6 +9,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; import org.jetbrains.java.decompiler.modules.decompiler.sforms.SFormsConstructor; @@ -307,7 +308,7 @@ public Exprent copy() { NewExprent ret = new NewExprent(newType, lst, bytecode); ret.setConstructor(constructor == null ? null : (InvocationExprent)constructor.copy()); - ret.setLstArrayElements(lstArrayElements); + ret.setLstArrayElements(DecHelper.copyExprentList(lstArrayElements)); ret.setDirectArrayInit(directArrayInit); ret.setAnonymous(anonymous); ret.setEnumConst(enumConst); diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 8c89946ed1..24b6fa435d 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -696,6 +696,7 @@ private void registerDefault() { register(JAVA_21, "TestSwitchOnEnumJ21", "ext/TestEnum2"); register(JAVA_21, "TestInnerClassesJ21"); register(JAVA_8, "TestInnerClassesJ8"); + register(JAVA_8, "TestSwitchInTry"); } private void registerEntireClassPath() { diff --git a/testData/results/pkg/TestSwitchInTry.dec b/testData/results/pkg/TestSwitchInTry.dec new file mode 100644 index 0000000000..b80405d1d0 --- /dev/null +++ b/testData/results/pkg/TestSwitchInTry.dec @@ -0,0 +1,96 @@ +package pkg; + +import java.util.Arrays; +import java.util.List; + +public class TestSwitchInTry { + public static List method(String[] args) { + String ret = "";// 13 + + for (String arg : args) {// 14 + try { + byte var7 = -1; + switch (arg.hashCode()) {// 16 + case 97: + if (arg.equals("a")) { + var7 = 0; + } + default: + switch (var7) { + case 0: + ret = "a";// 18 + return Arrays.asList(ret);// 24 + default: + return Arrays.asList(ret); + } + } + } catch (Exception var8) {// 21 + } + } + + return Arrays.asList(ret); + } +} + +class 'pkg/TestSwitchInTry' { + method 'method ([Ljava/lang/String;)Ljava/util/List;' { + 0 7 + 1 7 + 2 7 + 3 9 + 4 9 + 5 9 + 7 9 + 9 9 + a 9 + 15 9 + 16 9 + 17 12 + 18 12 + 1b 11 + 1c 11 + 1d 11 + 1e 12 + 1f 12 + 20 12 + 21 12 + 22 12 + 23 12 + 34 14 + 35 14 + 36 14 + 37 14 + 38 14 + 39 14 + 3a 14 + 3b 14 + 3e 15 + 3f 15 + 40 15 + 41 18 + 42 18 + 43 18 + 54 20 + 55 20 + 56 20 + 5a 26 + 5c 9 + 5d 9 + 5e 9 + 68 21 + 6a 21 + 6b 21 + 6c 21 + 6d 21 + } +} + +Lines mapping: +13 <-> 8 +14 <-> 10 +16 <-> 13 +18 <-> 21 +21 <-> 27 +24 <-> 22 +Not mapped: +20 diff --git a/testData/src/java8/pkg/TestSwitchInTry.java b/testData/src/java8/pkg/TestSwitchInTry.java new file mode 100644 index 0000000000..a7450667cf --- /dev/null +++ b/testData/src/java8/pkg/TestSwitchInTry.java @@ -0,0 +1,26 @@ +package pkg; + +import java.util.Arrays; +import java.util.List; + +public class TestSwitchInTry { + /** + * ISSUE 349: Infinite loop in SFormsConstructor->splitVariables + * Submitted by @mvisat + */ + + public static List method(String[] args) { + String ret = ""; + for (String arg : args) { + try { + switch (arg) { + case "a": + ret = "a"; + } + break; + } catch (Exception ex) { + } + } + return Arrays.asList(ret); + } +} From f631ae787168ef1b0f9c5c7a2214fb1dc718bc77 Mon Sep 17 00:00:00 2001 From: coehlrich Date: Sat, 6 Apr 2024 17:00:06 +1300 Subject: [PATCH 06/17] Fix switch expressions not generating (#358) * Fix if statements inside switch statements preventing switch expressions * Update test output * Fix switch statements that throw an exception not being converted * Add comment --- .../decompiler/SwitchExpressionHelper.java | 5 +- .../decompiler/TryWithResourcesProcessor.java | 15 +- .../java/decompiler/SingleClassesTest.java | 1 + .../pkg/TestReturnSwitchExpression5.dec | 36 ++++ testData/results/pkg/TestSwitchOnEnumJ21.dec | 196 +++++++++++------- .../pkg/TestReturnSwitchExpression5.java | 15 ++ .../src/java21/pkg/TestSwitchOnEnumJ21.java | 16 ++ 7 files changed, 201 insertions(+), 83 deletions(-) create mode 100644 testData/results/pkg/TestReturnSwitchExpression5.dec create mode 100644 testData/src/java16/pkg/TestReturnSwitchExpression5.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchExpressionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchExpressionHelper.java index d035a3f4d0..72b6e2e2fa 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchExpressionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SwitchExpressionHelper.java @@ -309,10 +309,9 @@ private static Map> mapAssignments(List exprents = breakJump.getExprents(); if (exprents != null && !exprents.isEmpty()) { - if (exprents.size() == 1 && exprents.get(0) instanceof ExitExprent) { - ExitExprent exit = ((ExitExprent) exprents.get(0)); + if (exprents.size() > 0 && exprents.get(exprents.size() - 1) instanceof ExitExprent exit) { - // Special case throws + // Last exprent throws instead of storing a value if (exit.getExitType() == ExitExprent.Type.THROW) { map.put(breakJump, null); continue; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/TryWithResourcesProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/TryWithResourcesProcessor.java index 84cbd2fa9c..2dd9daa0d0 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/TryWithResourcesProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/TryWithResourcesProcessor.java @@ -7,8 +7,11 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -348,14 +351,22 @@ public static void findEdgesLeaving(Statement curr, Statement check, Set edges, boolean allowExit) { + findEdgesLeaving(curr, check, edges, allowExit, new HashMap<>()); + } + + private static void findEdgesLeaving(Statement curr, Statement check, Set edges, boolean allowExit, Map> found) { for (StatEdge edge : curr.getAllSuccessorEdges()) { if (!check.containsStatement(edge.getDestination()) && (allowExit || !(edge.getDestination() instanceof DummyExitStatement))) { - edges.add(edge); + // Check if the edge is either explicit or isn't implicit to an already found edge + if (edge.explicit || found.entrySet().stream().allMatch(e -> !e.getKey().containsStatement(curr) || !e.getValue().contains(edge.getDestination()))) { + edges.add(edge); + found.computeIfAbsent(curr, stat -> new HashSet<>()).add(edge.getDestination()); + } } } for (Statement stat : curr.getStats()) { - findEdgesLeaving(stat, check, edges, allowExit); + findEdgesLeaving(stat, check, edges, allowExit, found); } } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 24b6fa435d..fa855e3847 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -389,6 +389,7 @@ private void registerDefault() { register(JAVA_16, "TestReturnSwitchExpression2"); register(JAVA_16, "TestReturnSwitchExpression3"); register(JAVA_16, "TestReturnSwitchExpression4"); + register(JAVA_16, "TestReturnSwitchExpression5"); register(JAVA_16, "TestSwitchExprString1"); register(JAVA_16, "TestConstructorSwitchExpression1"); diff --git a/testData/results/pkg/TestReturnSwitchExpression5.dec b/testData/results/pkg/TestReturnSwitchExpression5.dec new file mode 100644 index 0000000000..81ff5329e4 --- /dev/null +++ b/testData/results/pkg/TestReturnSwitchExpression5.dec @@ -0,0 +1,36 @@ +package pkg; + +public class TestReturnSwitchExpression5 { + public String test(int i) { + return switch (i) {// 6 + case 1 -> "1";// 7 + case 2 -> "2";// 8 + default -> { + int a = 0;// 10 + throw new RuntimeException();// 11 + } + }; + } +} + +class 'pkg/TestReturnSwitchExpression5' { + method 'test (I)Ljava/lang/String;' { + 0 4 + 1 4 + 1c 5 + 1d 5 + 21 6 + 22 6 + 26 8 + 27 8 + 2f 9 + 30 4 + } +} + +Lines mapping: +6 <-> 5 +7 <-> 6 +8 <-> 7 +10 <-> 9 +11 <-> 10 diff --git a/testData/results/pkg/TestSwitchOnEnumJ21.dec b/testData/results/pkg/TestSwitchOnEnumJ21.dec index a496dc6309..9cf0c1ccbd 100644 --- a/testData/results/pkg/TestSwitchOnEnumJ21.dec +++ b/testData/results/pkg/TestSwitchOnEnumJ21.dec @@ -37,42 +37,58 @@ public class TestSwitchOnEnumJ21 { }; } - public int testDefault(TestSwitchOnEnumJ21.TestEnum a) { + public int test5(TestSwitchOnEnumJ21.TestEnum a, boolean b) { return switch (a) {// 41 case A -> 1;// 42 - default -> 5;// 43 + case B -> 2;// 43 + case C -> { + if (b) {// 45 + boolean c = true;// 46 + yield 3;// 47 + } else { + boolean d = true;// 49 + yield 4;// 50 + } + } + }; + } + + public int testDefault(TestSwitchOnEnumJ21.TestEnum a) { + return switch (a) {// 57 + case A -> 1;// 58 + default -> 5;// 59 }; } public int testDefault2(TestEnum2 a) { - return switch (a) {// 48 - case A -> 1;// 49 - default -> 5;// 50 + return switch (a) {// 64 + case A -> 1;// 65 + default -> 5;// 66 }; } public void testStatement(TestSwitchOnEnumJ21.TestEnum a) { - switch (a) {// 55 + switch (a) {// 71 case A: - System.out.println("A");// 57 - break;// 58 + System.out.println("A");// 73 + break;// 74 case B: - System.out.println("B");// 60 - break;// 61 + System.out.println("B");// 76 + break;// 77 case C: - System.out.println("C");// 63 + System.out.println("C");// 79 } - }// 65 + }// 81 public void testStatementDefault(TestSwitchOnEnumJ21.TestEnum a) { - switch (a) {// 68 + switch (a) {// 84 case A: - System.out.println("A");// 70 - break;// 71 + System.out.println("A");// 86 + break;// 87 default: - System.out.println("C");// 73 + System.out.println("C");// 89 } - }// 75 + }// 91 static enum TestEnum { A, @@ -123,72 +139,88 @@ class 'pkg/TestSwitchOnEnumJ21' { 3f 31 } - method 'testDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)I' { + method 'test5 (Lpkg/TestSwitchOnEnumJ21$TestEnum;Z)I' { 0 40 4 40 - 18 41 - 1c 42 - 1d 40 + 2a 41 + 2e 42 + 32 44 + 33 44 + 36 45 + 37 45 + 38 46 + 3c 48 + 3d 48 + 3e 49 + 42 40 } - method 'testDefault2 (Lext/TestEnum2;)I' { - 3 47 - 7 47 - 8 47 - 1c 48 - 20 49 - 21 47 + method 'testDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)I' { + 0 56 + 4 56 + 18 57 + 1c 58 + 1d 56 } - method 'testStatement (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { - 0 54 - 4 54 - 20 56 - 21 56 - 22 56 - 23 56 - 24 56 - 25 56 - 26 56 - 27 56 - 28 57 - 2b 59 - 2c 59 - 2d 59 - 2e 59 - 2f 59 - 30 59 - 31 59 - 32 59 - 33 60 - 36 62 - 37 62 - 38 62 - 39 62 - 3a 62 - 3b 62 - 3e 64 + method 'testDefault2 (Lext/TestEnum2;)I' { + 3 63 + 7 63 + 8 63 + 1c 64 + 20 65 + 21 63 } - method 'testStatementDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { - 0 67 - 4 67 - 18 69 - 19 69 - 1a 69 - 1b 69 - 1c 69 - 1d 69 - 1e 69 - 1f 69 - 20 70 + method 'testStatement (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { + 0 70 + 4 70 + 20 72 + 21 72 + 22 72 23 72 24 72 25 72 26 72 27 72 - 28 72 - 2b 74 + 28 73 + 2b 75 + 2c 75 + 2d 75 + 2e 75 + 2f 75 + 30 75 + 31 75 + 32 75 + 33 76 + 36 78 + 37 78 + 38 78 + 39 78 + 3a 78 + 3b 78 + 3e 80 + } + + method 'testStatementDefault (Lpkg/TestSwitchOnEnumJ21$TestEnum;)V' { + 0 83 + 4 83 + 18 85 + 19 85 + 1a 85 + 1b 85 + 1c 85 + 1d 85 + 1e 85 + 1f 85 + 20 86 + 23 88 + 24 88 + 25 88 + 26 88 + 27 88 + 28 88 + 2b 90 } } @@ -214,18 +246,26 @@ Lines mapping: 41 <-> 41 42 <-> 42 43 <-> 43 -48 <-> 48 +45 <-> 45 +46 <-> 46 +47 <-> 47 49 <-> 49 50 <-> 50 -55 <-> 55 57 <-> 57 58 <-> 58 -60 <-> 60 -61 <-> 61 -63 <-> 63 +59 <-> 59 +64 <-> 64 65 <-> 65 -68 <-> 68 -70 <-> 70 +66 <-> 66 71 <-> 71 73 <-> 73 -75 <-> 75 +74 <-> 74 +76 <-> 76 +77 <-> 77 +79 <-> 79 +81 <-> 81 +84 <-> 84 +86 <-> 86 +87 <-> 87 +89 <-> 89 +91 <-> 91 diff --git a/testData/src/java16/pkg/TestReturnSwitchExpression5.java b/testData/src/java16/pkg/TestReturnSwitchExpression5.java new file mode 100644 index 0000000000..674f24656b --- /dev/null +++ b/testData/src/java16/pkg/TestReturnSwitchExpression5.java @@ -0,0 +1,15 @@ +package pkg; + + +public class TestReturnSwitchExpression5 { + public String test(int i) { + return switch (i) { + case 1 -> "1"; + case 2 -> "2"; + default -> { + int a = 0; + throw new RuntimeException(); + } + }; + } +} diff --git a/testData/src/java21/pkg/TestSwitchOnEnumJ21.java b/testData/src/java21/pkg/TestSwitchOnEnumJ21.java index 1aff25e24e..aa59f277f2 100644 --- a/testData/src/java21/pkg/TestSwitchOnEnumJ21.java +++ b/testData/src/java21/pkg/TestSwitchOnEnumJ21.java @@ -37,6 +37,22 @@ public int test4(TestEnum2 a) { }; } + public int test5(TestEnum a, boolean b) { + return switch (a) { + case A -> 1; + case B -> 2; + case C -> { + if (b) { + boolean c = true; + yield 3; + } else { + boolean d = true; + yield 4; + } + } + }; + } + public int testDefault(TestEnum a) { return switch (a) { case A -> 1; From a478d8b5a50148d74512382fe464e67d8c5f39f2 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:54:28 -0700 Subject: [PATCH 07/17] Fix formatter emitting blank lines for long method invocations with no arguments (#362) * Fix formatter emitting blank lines for long method invocations with no arguments fixes #359 * Simplify test --- .../decompiler/exps/InvocationExprent.java | 19 ++++++++++++------- .../java/decompiler/SingleClassesTest.java | 1 + testData/results/pkg/TestBlankLinesSpaces.dec | 19 +++++++++++++++++++ .../src/java8/pkg/TestBlankLinesSpaces.java | 9 +++++++++ 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 testData/results/pkg/TestBlankLinesSpaces.dec create mode 100644 testData/src/java8/pkg/TestBlankLinesSpaces.java diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 9edebaae76..c80f066721 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -949,8 +949,6 @@ private static void appendBootstrapArgument(TextBuffer buf, PooledConstant arg) } public TextBuffer appendParamList(int indent) { - TextBuffer buf = new TextBuffer(); - buf.pushNewlineGroup(indent, 1); List mask = null; boolean isEnum = false; if (functype == Type.INIT) { @@ -1105,9 +1103,14 @@ else if (inv.isUnboxingCall() && !inv.shouldForceUnboxing()) { } + TextBuffer buf = new TextBuffer(); + boolean firstParameter = true; - buf.appendPossibleNewline(); - buf.pushNewlineGroup(indent, 0); + if (!lstParameters.isEmpty()) { + buf.pushNewlineGroup(indent, 1); + buf.appendPossibleNewline(); + buf.pushNewlineGroup(indent, 0); + } for (int i = start; i < lstParameters.size(); i++) { if (mask == null || mask.get(i) == null) { @@ -1161,9 +1164,11 @@ else if (desc != null && desc.getSignature() != null && genericArgs.size() != 0) } } - buf.popNewlineGroup(); - buf.appendPossibleNewline("", true); - buf.popNewlineGroup(); + if (!lstParameters.isEmpty()) { + buf.popNewlineGroup(); + buf.appendPossibleNewline("", true); + buf.popNewlineGroup(); + } return buf; } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index fa855e3847..d01beec1bd 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -325,6 +325,7 @@ private void registerDefault() { register(JAVA_8, "TestLongMethodDeclaration"); register(JAVA_8, "TestLongMethodInvocation"); register(JAVA_8, "TestBinaryOperationWrapping"); + register(JAVA_8, "TestBlankLinesSpaces"); register(JAVA_8, "TestLoopBreak"); register(JAVA_8, "TestLoopBreak2"); register(JAVA_8, "TestSimpleWhile"); diff --git a/testData/results/pkg/TestBlankLinesSpaces.dec b/testData/results/pkg/TestBlankLinesSpaces.dec new file mode 100644 index 0000000000..de70870546 --- /dev/null +++ b/testData/results/pkg/TestBlankLinesSpaces.dec @@ -0,0 +1,19 @@ +package pkg; + +public class TestBlankLinesSpaces { + private static final String STRING = longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(); + + private static String longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() { + return null;// 7 + } +} + +class 'pkg/TestBlankLinesSpaces' { + method 'longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ()Ljava/lang/String;' { + 0 6 + 1 6 + } +} + +Lines mapping: +7 <-> 7 diff --git a/testData/src/java8/pkg/TestBlankLinesSpaces.java b/testData/src/java8/pkg/TestBlankLinesSpaces.java new file mode 100644 index 0000000000..0ff0f98090 --- /dev/null +++ b/testData/src/java8/pkg/TestBlankLinesSpaces.java @@ -0,0 +1,9 @@ +package pkg; + +public class TestBlankLinesSpaces { + private static final String STRING = longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(); + + private static String longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() { + return null; + } +} From 4379782fc4c5fb1e962a0802779a88433f38dd27 Mon Sep 17 00:00:00 2001 From: coehlrich Date: Tue, 9 Apr 2024 12:50:06 +1200 Subject: [PATCH 08/17] Fix variable namer parent not being set for local classes (#361) * Fix local class methods not getting the parent context * Add test --- .../VariableRenamingTest.java | 1 + .../results/pkg/TestJADLocalClasses.dec | 38 +++++++++++++++++++ .../src/java8/pkg/TestJADLocalClasses.java | 12 ++++++ .../decompiler/vars/VarDefinitionHelper.java | 30 ++++++++------- 4 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 plugins/variable-renaming/testData/results/pkg/TestJADLocalClasses.dec create mode 100644 plugins/variable-renaming/testData/src/java8/pkg/TestJADLocalClasses.java diff --git a/plugins/variable-renaming/src/test/java/org/vineflower/variablerenaming/VariableRenamingTest.java b/plugins/variable-renaming/src/test/java/org/vineflower/variablerenaming/VariableRenamingTest.java index ab969a6907..9e8118912d 100644 --- a/plugins/variable-renaming/src/test/java/org/vineflower/variablerenaming/VariableRenamingTest.java +++ b/plugins/variable-renaming/src/test/java/org/vineflower/variablerenaming/VariableRenamingTest.java @@ -13,6 +13,7 @@ protected void registerAll() { register(JAVA_8, "TestJADNaming"); // TODO: loop part fails registerRaw(CUSTOM, "TestJadLvtCollision"); // created by placing a class in java8 sources and remapping its param using tinyremapper + register(JAVA_8, "TestJADLocalClasses"); }, IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1", IFernflowerPreferences.DUMP_ORIGINAL_LINES, "1", IFernflowerPreferences.DUMP_EXCEPTION_ON_ERROR, "0", diff --git a/plugins/variable-renaming/testData/results/pkg/TestJADLocalClasses.dec b/plugins/variable-renaming/testData/results/pkg/TestJADLocalClasses.dec new file mode 100644 index 0000000000..117c32ede0 --- /dev/null +++ b/plugins/variable-renaming/testData/results/pkg/TestJADLocalClasses.dec @@ -0,0 +1,38 @@ +package pkg; + +public class TestJADLocalClasses { + public void function() { + int i = 0;// 5 + + class Test { + Test() { + int j = 0;// 8 + }// 9 + } + + }// 11 +} + +class 'pkg/TestJADLocalClasses' { + method 'function ()V' { + 0 4 + 1 4 + 2 12 + } +} + +class 'pkg/TestJADLocalClasses$1Test' { + method ' (Lpkg/TestJADLocalClasses;)V' { + 9 8 + a 8 + b 9 + } +} + +Lines mapping: +5 <-> 5 +8 <-> 9 +9 <-> 10 +11 <-> 13 +Not mapped: +7 diff --git a/plugins/variable-renaming/testData/src/java8/pkg/TestJADLocalClasses.java b/plugins/variable-renaming/testData/src/java8/pkg/TestJADLocalClasses.java new file mode 100644 index 0000000000..baa49e221b --- /dev/null +++ b/plugins/variable-renaming/testData/src/java8/pkg/TestJADLocalClasses.java @@ -0,0 +1,12 @@ +package pkg; + +public class TestJADLocalClasses { + public void function() { + int a = 0; + class Test { + Test() { + int b = 0; + } + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index e751bf0af5..97fc30c16f 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -25,6 +25,7 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.util.ArrayHelper; +import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.Pair; import org.jetbrains.java.decompiler.util.StatementIterator; @@ -1028,18 +1029,11 @@ private void propogateLVTs(Statement stat) { Map renames = this.mt.getVariableNamer().rename(typeNames); + Set methods = new HashSet<>(); + // Stuff the parent context into enclosed child methods StatementIterator.iterate(root, (exprent) -> { - List methods = new ArrayList<>(); - if (exprent instanceof VarExprent) { - VarExprent var = (VarExprent)exprent; - if (var.isClassDef()) { - ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(var.getVarType().value); - if (child != null) - methods.addAll(child.classStruct.getMethods()); - } - } - else if (exprent instanceof NewExprent) { + if (exprent instanceof NewExprent) { NewExprent _new = (NewExprent)exprent; if (_new.isAnonymous()) { //TODO: Check for Lambda here? ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(_new.getNewType().value); @@ -1056,13 +1050,21 @@ else if (exprent instanceof NewExprent) { } } } - - for (StructMethod meth : methods) { - meth.getVariableNamer().addParentContext(VarDefinitionHelper.this.mt.getVariableNamer()); - } return 0; }); + // Local classes aren't added into the method body yet + String thisKey = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()); + for (ClassNode nested : DecompilerContext.getClassProcessor().getMapRootClasses().get(mt.getClassQualifiedName()).nested) { + if (nested.type == ClassNode.Type.LOCAL && thisKey.equals(nested.enclosingMethod)) { + methods.addAll(nested.classStruct.getMethods()); + } + } + + for (StructMethod meth : methods) { + meth.getVariableNamer().addParentContext(VarDefinitionHelper.this.mt.getVariableNamer()); + } + Map lvts = new HashMap<>(); for (Entry e : types.entrySet()) { From 31554b448a7163b481f5709e0678af6ae525ba9f Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:47:29 -0700 Subject: [PATCH 09/17] Don't append indent to newlines immediately followed by another newline (#363) fixes #359 (for real this time?) --- .../java/decompiler/util/TextBuffer.java | 9 ++- testData/results/pkg/TestBlankLinesSpaces.dec | 79 ++++++++++++++++++- .../src/java8/pkg/TestBlankLinesSpaces.java | 16 ++++ 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/util/TextBuffer.java b/src/org/jetbrains/java/decompiler/util/TextBuffer.java index 7fe20cd28a..5c5e435427 100644 --- a/src/org/jetbrains/java/decompiler/util/TextBuffer.java +++ b/src/org/jetbrains/java/decompiler/util/TextBuffer.java @@ -433,10 +433,13 @@ private void reformatGroup(NewlineGroup group, List offsetMapping, int // add extra indent after newlines if (pos + offset + myLineSeparator.length() < myStringBuilder.length() && myStringBuilder.substring(pos + offset, pos + offset + myLineSeparator.length()).equals(myLineSeparator)) { - for (int i = 0; i < extraIndent; i++) { - myStringBuilder.insert(pos + offset + myLineSeparator.length(), myIndent); + // not for blank lines + if (!myStringBuilder.substring(pos + offset + myLineSeparator.length(), pos + offset + myLineSeparator.length() * 2).equals(myLineSeparator)) { + for (int i = 0; i < extraIndent; i++) { + myStringBuilder.insert(pos + offset + myLineSeparator.length(), myIndent); + } + offset += myIndent.length() * extraIndent; } - offset += myIndent.length() * extraIndent; } // do multiple passes in an inner loop, as there could be arbitrarily many with the same offset diff --git a/testData/results/pkg/TestBlankLinesSpaces.dec b/testData/results/pkg/TestBlankLinesSpaces.dec index de70870546..c716ca4bc9 100644 --- a/testData/results/pkg/TestBlankLinesSpaces.dec +++ b/testData/results/pkg/TestBlankLinesSpaces.dec @@ -1,19 +1,90 @@ package pkg; +import java.util.Random; + public class TestBlankLinesSpaces { private static final String STRING = longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(); private static String longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() { - return null;// 7 + return null;// 9 } + + static void takeLambda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000( + Runnable r + ) { + }// 24 + + static { + takeLambda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(// 13 + () -> { + for (int i = 0; i < 10; i++) {// 14 + System.out.println(i);// 15 + } + + if (new Random().nextBoolean()) {// 17 + System.out.println(true);// 18 + } + }// 20 + ); + }// 21 } class 'pkg/TestBlankLinesSpaces' { method 'longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ()Ljava/lang/String;' { - 0 6 - 1 6 + 0 8 + 1 8 + } + + method 'takeLambda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 (Ljava/lang/Runnable;)V' { + 0 14 + } + + method ' ()V' { + b 17 + c 17 + d 17 + e 28 + } + + method 'lambda$static$0 ()V' { + 0 19 + 1 19 + 2 19 + 3 19 + 4 19 + 5 19 + 8 20 + 9 20 + a 20 + b 20 + c 20 + d 20 + e 20 + f 19 + 10 19 + 11 19 + 1c 23 + 1d 23 + 1e 23 + 1f 23 + 22 24 + 23 24 + 24 24 + 25 24 + 26 24 + 29 26 } } Lines mapping: -7 <-> 7 +9 <-> 9 +13 <-> 18 +14 <-> 20 +15 <-> 21 +17 <-> 24 +18 <-> 25 +20 <-> 27 +21 <-> 29 +24 <-> 15 +Not mapped: +6 diff --git a/testData/src/java8/pkg/TestBlankLinesSpaces.java b/testData/src/java8/pkg/TestBlankLinesSpaces.java index 0ff0f98090..efba634512 100644 --- a/testData/src/java8/pkg/TestBlankLinesSpaces.java +++ b/testData/src/java8/pkg/TestBlankLinesSpaces.java @@ -1,9 +1,25 @@ package pkg; +import java.util.Random; + public class TestBlankLinesSpaces { private static final String STRING = longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(); private static String longMethodName00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() { return null; } + + static { + takeLambda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(() -> { + for (int i = 0; i < 10; i++) { + System.out.println(i); + } + if (new Random().nextBoolean()) { + System.out.println(true); + } + }); + } + + static void takeLambda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(Runnable r) { + } } From bd3956564b9544a5dca195febf626cc6a7e48eb2 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:59:48 -0700 Subject: [PATCH 10/17] Add missing range check to account for EOF (#364) --- src/org/jetbrains/java/decompiler/util/TextBuffer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/java/decompiler/util/TextBuffer.java b/src/org/jetbrains/java/decompiler/util/TextBuffer.java index 5c5e435427..7861d753a4 100644 --- a/src/org/jetbrains/java/decompiler/util/TextBuffer.java +++ b/src/org/jetbrains/java/decompiler/util/TextBuffer.java @@ -434,7 +434,7 @@ private void reformatGroup(NewlineGroup group, List offsetMapping, int // add extra indent after newlines if (pos + offset + myLineSeparator.length() < myStringBuilder.length() && myStringBuilder.substring(pos + offset, pos + offset + myLineSeparator.length()).equals(myLineSeparator)) { // not for blank lines - if (!myStringBuilder.substring(pos + offset + myLineSeparator.length(), pos + offset + myLineSeparator.length() * 2).equals(myLineSeparator)) { + if (!(pos + offset + myLineSeparator.length() * 2 < myStringBuilder.length()) || !myStringBuilder.substring(pos + offset + myLineSeparator.length(), pos + offset + myLineSeparator.length() * 2).equals(myLineSeparator)) { for (int i = 0; i < extraIndent; i++) { myStringBuilder.insert(pos + offset + myLineSeparator.length(), myIndent); } From 8c41ea642c3a780ee5d0959790da1cefa115c4d7 Mon Sep 17 00:00:00 2001 From: shartte Date: Wed, 10 Apr 2024 22:42:23 +0200 Subject: [PATCH 11/17] Only use parameter names from method parameter attributes, if they are non-null (#365) --- src/org/jetbrains/java/decompiler/main/ClassWriter.java | 2 +- testData/results/pkg/TestInnerClassesJ21.dec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 22e9ca34eb..8fdcc90d16 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -1208,7 +1208,7 @@ else if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarT String clashingName = methodWrapper.varproc.getClashingName(new VarVersionPair(index, 0)); if (clashingName != null) { parameterName = clashingName; - } else if (methodParameters != null && i < methodParameters.size()) { + } else if (methodParameters != null && i < methodParameters.size() && methodParameters.get(i).myName != null) { parameterName = methodParameters.get(i).myName; } else { parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); diff --git a/testData/results/pkg/TestInnerClassesJ21.dec b/testData/results/pkg/TestInnerClassesJ21.dec index 98eebf7bde..8ffaffbfa9 100644 --- a/testData/results/pkg/TestInnerClassesJ21.dec +++ b/testData/results/pkg/TestInnerClassesJ21.dec @@ -10,7 +10,7 @@ public class TestInnerClassesJ21 { private final long z; private final String v; - public TestInner(int param2, long param3, String param5) { + public TestInner(int x, long z, String v) { this.x = x;// 11 this.z = z;// 12 this.v = v;// 13 From fdc9933f2ea604820441bffaeaadf9e2475b6995 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:59:58 -0700 Subject: [PATCH 12/17] Don't tear down nested class wrappers after writing the root class (#366) They may be needed later when writing other classes. This fixes an issue where non-static inner class constructor invocations from foreign classes would inconsistently have the synthetic enclosing this instance removed from the parameter list, depending on the order they were processed in (and consequently having strange interactions with multithreaded writing) --- src/org/jetbrains/java/decompiler/main/ClassesProcessor.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 287ce0f5cd..a5c5c5818b 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -563,9 +563,8 @@ private static void destroyWrappers(ClassNode node) { node.classStruct.releaseResources(); node.classStruct.getMethods().forEach(m -> m.clearVariableNamer()); - for (ClassNode nd : node.nested) { - destroyWrappers(nd); - } + // Don't destroy inner node wrappers, they may still be needed to process constructor + // invocations etc. from other classes. } public Map getMapRootClasses() { From 140d738cc19296d535f07334a8059bac9217309f Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Thu, 11 Apr 2024 22:05:26 -0400 Subject: [PATCH 13/17] Fix J21 nested inner class synthetic params --- .../main/rels/NestedClassProcessor.java | 2 +- .../java/decompiler/SingleClassesTest.java | 1 + testData/results/pkg/TestInnerClasses2J21.dec | 43 +++++++++++++++++++ .../src/java21/pkg/TestInnerClasses2J21.java | 15 +++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 testData/results/pkg/TestInnerClasses2J21.dec create mode 100644 testData/src/java21/pkg/TestInnerClasses2J21.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 739e78c61c..6670dc6921 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -807,7 +807,7 @@ private static String getEnclosingVarField(StructClass cl, MethodWrapper method, StructMethodParametersAttribute attr = method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS); List entries = attr.getEntries(); - if (outerIdx < entries.size() && (entries.get(outerIdx).myAccessFlags & CodeConstants.ACC_MANDATED) == CodeConstants.ACC_MANDATED) { + if (outerIdx < entries.size() && (entries.get(outerIdx).myAccessFlags & (CodeConstants.ACC_MANDATED | CodeConstants.ACC_SYNTHETIC)) != 0) { String name = method.varproc.getVarName(var); VarType type = method.varproc.getVarType(var); diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index d01beec1bd..7eb4e40b9b 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -697,6 +697,7 @@ private void registerDefault() { register(JAVA_8, "TestDanglingBoxingCall"); register(JAVA_21, "TestSwitchOnEnumJ21", "ext/TestEnum2"); register(JAVA_21, "TestInnerClassesJ21"); + register(JAVA_21, "TestInnerClasses2J21"); register(JAVA_8, "TestInnerClassesJ8"); register(JAVA_8, "TestSwitchInTry"); } diff --git a/testData/results/pkg/TestInnerClasses2J21.dec b/testData/results/pkg/TestInnerClasses2J21.dec new file mode 100644 index 0000000000..4cbd42fef5 --- /dev/null +++ b/testData/results/pkg/TestInnerClasses2J21.dec @@ -0,0 +1,43 @@ +package pkg; + +public class TestInnerClasses2J21 { + private void test() { + new TestInnerClasses2J21.Inner().new Inner2(true, true);// 5 + }// 6 + + private class Inner { + private class Inner2 { + private Inner2(boolean nonFinal, final boolean finalB) { + TestInnerClasses2J21.this.test();// 11 + }// 12 + } + } +} + +class 'pkg/TestInnerClasses2J21' { + method 'test ()V' { + c 4 + d 4 + 12 5 + } +} + +class 'pkg/TestInnerClasses2J21$Inner$Inner2' { + method ' (Lpkg/TestInnerClasses2J21$Inner;ZZ)V' { + 5 10 + 6 10 + 7 10 + 8 10 + 9 10 + a 10 + b 11 + } +} + +Lines mapping: +5 <-> 5 +6 <-> 6 +11 <-> 11 +12 <-> 12 +Not mapped: +10 \ No newline at end of file diff --git a/testData/src/java21/pkg/TestInnerClasses2J21.java b/testData/src/java21/pkg/TestInnerClasses2J21.java new file mode 100644 index 0000000000..d856d67cd7 --- /dev/null +++ b/testData/src/java21/pkg/TestInnerClasses2J21.java @@ -0,0 +1,15 @@ +package pkg; + +public class TestInnerClasses2J21 { + private void test() { + new Inner().new Inner2(true, true); + } + + private class Inner { + private class Inner2 { + private Inner2(boolean nonFinal, final boolean finalB) { + TestInnerClasses2J21.this.test(); + } + } + } +} From 08608f981f1ed6a0a2f90cea2cf9b935395e685f Mon Sep 17 00:00:00 2001 From: Iota <47987888+IotaBread@users.noreply.github.com> Date: Thu, 11 Apr 2024 22:29:30 -0400 Subject: [PATCH 14/17] Don't write explicit casts where redundant (#341) * Don't write explicit casts where redundant * Fix numeric parameter disambiguation * Write casts when not doing so would change behavior * Fix failing test --------- Co-authored-by: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> --- .../results/pkg/TestTailrecFunctions.dec | 8 +- .../decompiler/SecondaryFunctionsHelper.java | 61 +++ .../decompiler/exps/FunctionExprent.java | 12 +- .../decompiler/exps/InvocationExprent.java | 8 +- .../decompiler/struct/gen/TypeFamily.java | 22 +- .../java/decompiler/SingleClassesTest.java | 7 +- .../pkg/TestAssignmentSwitchExpression3.dec | 4 +- testData/results/pkg/TestClassVar.dec | 2 +- testData/results/pkg/TestDebugSymbols.dec | 2 +- .../results/pkg/TestDefiniteAssignment.dec | 438 +++++++++--------- .../results/pkg/TestDoublePopAfterJump.dec | 2 +- testData/results/pkg/TestLocalsNames.dec | 2 +- testData/results/pkg/TestMethodHandles.dec | 10 +- .../pkg/TestMixedCompoundAssignment.dec | 60 +-- testData/results/pkg/TestMultiCast.dec | 2 +- .../results/pkg/TestMultipleStaticBlocks.dec | 4 +- testData/results/pkg/TestNumberCasts.dec | 367 +++++++++++++++ .../results/pkg/TestNumberDisambiguation.dec | 384 +++++++++++++++ testData/results/pkg/TestObjectBitwise.dec | 20 +- testData/results/pkg/TestRecordPattern3.dec | 2 +- testData/results/pkg/TestStackCastParam.dec | 4 +- testData/results/pkg/TestWhileTernary10.dec | 8 +- testData/src/java8/pkg/TestNumberCasts.java | 71 +++ .../java8/pkg/TestNumberDisambiguation.java | 93 ++++ 24 files changed, 1295 insertions(+), 298 deletions(-) create mode 100644 testData/results/pkg/TestNumberCasts.dec create mode 100644 testData/results/pkg/TestNumberDisambiguation.dec create mode 100644 testData/src/java8/pkg/TestNumberCasts.java create mode 100644 testData/src/java8/pkg/TestNumberDisambiguation.java diff --git a/plugins/kotlin/testData/results/pkg/TestTailrecFunctions.dec b/plugins/kotlin/testData/results/pkg/TestTailrecFunctions.dec index 39e7eeff61..4335d2ebed 100644 --- a/plugins/kotlin/testData/results/pkg/TestTailrecFunctions.dec +++ b/plugins/kotlin/testData/results/pkg/TestTailrecFunctions.dec @@ -75,9 +75,9 @@ class TestTailrecFunctions { var var12: Long = acc; while (var10 != 0L) {// 44 - if (var10 % (long)2 == 0L) {// 45 + if (var10 % 2 == 0L) {// 45 var var21: Long = var8 * var8; - var var23: Long = var10 / (long)2; + var var23: Long = var10 / 2; var7 = var7; var8 = var21; var10 = var23; @@ -117,13 +117,13 @@ class TestTailrecFunctions { break; } - if (var8 % (long)2 != 0L) {// 51 + if (var8 % 2 != 0L) {// 51 var10000 = var6 * var5.fastPow(var6, var8 - 1L);// 52 break; } var var15: Long = var6 * var6; - var var17: Long = var8 / (long)2; + var var17: Long = var8 / 2; var5 = var5; var6 = var15; var8 = var17; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java index 142cee1ea6..b9056afa06 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -12,6 +12,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.gen.CodeType; +import org.jetbrains.java.decompiler.struct.gen.TypeFamily; import org.jetbrains.java.decompiler.struct.gen.VarType; import java.util.*; @@ -402,6 +403,57 @@ private static Exprent identifySecondaryFunctions(Exprent exprent, boolean state return new FunctionExprent(FunctionType.TERNARY, Arrays.asList( head, new ConstExprent(VarType.VARTYPE_INT, 0, null), iff), fexpr.bytecode); + case I2B: + case I2C: + case I2S: + if (lstOperands.get(0) instanceof FunctionExprent) { + FunctionExprent innerFunction = (FunctionExprent) lstOperands.get(0); + VarType castType = innerFunction.getFuncType().castType; + if (castType == VarType.VARTYPE_INT) { + // longs, floats and doubles are converted to ints before being converted to bytes, shorts or chars + innerFunction.setNeedsCast(false); + return ret; + } + } + // fallthrough + case I2L: + case I2F: + case I2D: + case L2F: + case L2D: + case F2D: + VarType exprType = lstOperands.get(0).getExprType(); + VarType castType = fexpr.getSimpleCastType(); + + // Simplify widening cast + if (castType.typeFamily == TypeFamily.INTEGER) { + if (castType.isStrictSuperset(exprType)) { + fexpr.setNeedsCast(false); + return ret; + } + } else if (castType.typeFamily.isGreater(exprType.typeFamily)) { + fexpr.setNeedsCast(false); + return ret; + } + break; + case DIV: + Exprent left = lstOperands.get(0); + boolean leftImplicitCast = left instanceof FunctionExprent && ((FunctionExprent) left).getSimpleCastType() != null && !((FunctionExprent) left).doesCast(); + Exprent right = lstOperands.get(1); + boolean rightImplicitCast = right instanceof FunctionExprent && ((FunctionExprent) right).getSimpleCastType() != null && !((FunctionExprent) right).doesCast(); + + if (leftImplicitCast && rightImplicitCast && right.getExprType() == left.getExprType()) { + // Only a single cast is needed explicitly + ((FunctionExprent) left).setNeedsCast(true); + } + break; + case SHL: + case SHR: + case USHR: + Exprent op = lstOperands.get(0); + if (op instanceof FunctionExprent && ((FunctionExprent) op).getSimpleCastType() != null && !((FunctionExprent) op).doesCast()) { + ((FunctionExprent) op).setNeedsCast(true); + } } break; case ASSIGNMENT: // check for conditional assignment @@ -464,6 +516,15 @@ private static Exprent identifySecondaryFunctions(Exprent exprent, boolean state } break; case INVOCATION: + InvocationExprent invocationExpr = (InvocationExprent) exprent; + if (invocationExpr.isBoxingCall()) { + Exprent param = invocationExpr.getLstParameters().get(0); + // Keep casts of boxed exprents + if (param instanceof FunctionExprent && ((FunctionExprent) param).getSimpleCastType() != null && !((FunctionExprent) param).doesCast()) { + ((FunctionExprent) param).setNeedsCast(true); + } + } + if (!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once). Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent); if (!exprent.equals(retexpr)) { diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java index 43135b8997..788a98da35 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java @@ -3,7 +3,6 @@ */ package org.jetbrains.java.decompiler.modules.decompiler.exps; -import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.plugins.PluginImplementationException; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -663,9 +662,12 @@ else if (left instanceof ConstExprent) { inv.forceUnboxing(true); } } - return buf.append(wrapOperandString(lstOperands.get(0), true, indent)) - .prepend("(" + ExprProcessor.getTypeName(funcType.castType) + ")") - .addTypeNameToken(funcType.castType, 1); + + if (!needsCast) { + return buf.append(lstOperands.get(0).toJava(indent)); + } + + return buf.append(ExprProcessor.getTypeName(funcType.castType)).encloseWithParens().append(wrapOperandString(lstOperands.get(0), true, indent)); } // return ""; @@ -695,7 +697,7 @@ public void unwrapBox() { @Override public int getPrecedence() { - if (funcType == FunctionType.CAST && !doesCast()) { + if ((funcType == FunctionType.CAST || funcType.castType != null) && !doesCast()) { return lstOperands.get(0).getPrecedence(); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index c80f066721..541d6a4744 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -1338,7 +1338,9 @@ private List getMatchedDescriptors() { private boolean matches(VarType[] left, VarType[] right) { if (left.length == right.length) { for (int i = 0; i < left.length; i++) { - if (left[i].typeFamily != right[i].typeFamily) { + TypeFamily leftFamily = left[i].typeFamily; + TypeFamily rightFamily = right[i].typeFamily; + if (leftFamily != rightFamily && !(leftFamily.isNumeric() && rightFamily.isNumeric())) { return false; } @@ -1406,6 +1408,10 @@ private BitSet getAmbiguousParameters(List matches) { boolean exact = true; for (int i = 0; i < md.params.length; i++) { Exprent exp = lstParameters.get(i); + if (exp instanceof FunctionExprent && ((FunctionExprent) exp).getSimpleCastType() != null) { + ((FunctionExprent) exp).setNeedsCast(true); + } + // Check if the current parameters and method descriptor are of the same type, or if the descriptor's type is a superset of the parameter's type. // This check ensures that parameters that can be safely passed don't have an unneeded cast on them, such as System.out.println((int)5);. // TODO: The root cause of the above issue seems to be threading related- When debugging line by line it doesn't cast, but when running normally it does. More digging needs to be done to figure out why this happens. diff --git a/src/org/jetbrains/java/decompiler/struct/gen/TypeFamily.java b/src/org/jetbrains/java/decompiler/struct/gen/TypeFamily.java index 2006596f51..e7929c32cc 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/TypeFamily.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/TypeFamily.java @@ -5,12 +5,26 @@ public enum TypeFamily { UNKNOWN, BOOLEAN, - INTEGER, - FLOAT, - LONG, - DOUBLE, + INTEGER(true), + FLOAT(true), + LONG(true), + DOUBLE(true), OBJECT; + private final boolean numeric; + + TypeFamily() { + this(false); + } + + TypeFamily(boolean numeric) { + this.numeric = numeric; + } + + public boolean isNumeric() { + return numeric; + } + // TODO: document what these mean, and try to remove! Doesn't make sense to have these public boolean isGreater(@NotNull TypeFamily other) { diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 7eb4e40b9b..abfaac27ff 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -253,7 +253,6 @@ private void registerDefault() { register(JAVA_16, "TestRecordAnno"); register(JAVA_16, "TestRecordBig"); register(JAVA_16, "TestRecordGenericSuperclass"); - // TODO: The (double) in front of the (int) should be removed register(JAVA_8, "TestMultiCast"); // TODO: some tests don't have proper if else chains register(JAVA_8, "TestComplexIfElseChain"); @@ -621,7 +620,7 @@ private void registerDefault() { // TODO: order of additions is wrong. Addition over floats isn't associative. // Derived from IDEA-291735 register(JAVA_8, "TestFloatOrderOfOperations"); - // TODO: many unnecessary casts, and not simplifying to `+=` + // TODO: not simplifying to `+=` register(JAVA_8, "TestMixedCompoundAssignment"); register(JAVA_8, "TestForeachVardef"); register(JAVA_8, "TestGenericStaticCall"); @@ -635,7 +634,6 @@ private void registerDefault() { register(JAVA_8, "TestTryCatchNested"); register(JAVA_8, "TestSwitchTernary"); register(JAVA_8, "TestBooleanExpressions"); - // TODO: cast not created, incorrect register(JAVA_8, "TestObjectBitwise"); register(JAVA_17, "TestSealedFinal", "SealedInterface"); register(JAVA_17, "TestSealedRecord", "SealedInterface"); @@ -694,6 +692,9 @@ private void registerDefault() { register(JAVA_21_PREVIEW, "TestCustomProcessor"); register(JAVA_16, "TestMissingLambdaBody"); register(JAVA_21_PREVIEW, "TestUnnamedVar1"); + register(JAVA_8, "TestNumberCasts"); + // TODO: Disambiguate only the required parameters + register(JAVA_8, "TestNumberDisambiguation"); register(JAVA_8, "TestDanglingBoxingCall"); register(JAVA_21, "TestSwitchOnEnumJ21", "ext/TestEnum2"); register(JAVA_21, "TestInnerClassesJ21"); diff --git a/testData/results/pkg/TestAssignmentSwitchExpression3.dec b/testData/results/pkg/TestAssignmentSwitchExpression3.dec index 489a68cb39..72f0c7bd5f 100644 --- a/testData/results/pkg/TestAssignmentSwitchExpression3.dec +++ b/testData/results/pkg/TestAssignmentSwitchExpression3.dec @@ -7,11 +7,11 @@ public class TestAssignmentSwitchExpression3 { Random random = switch (x) {// 7 case -5, -4, -3, -2, -1 -> { int seed = x >> 2;// 14 - yield new Random((long)seed);// 15 + yield new Random(seed);// 15 } default -> throw new IllegalStateException("Unexpected value: " + x);// 17 case 1, 2, 3, 4, 5 -> { - long seed = System.currentTimeMillis() - (long)(x * 1000);// 9 + long seed = System.currentTimeMillis() - x * 1000;// 9 yield new Random(seed);// 10 } case 6, 7, 8, 9, 10 -> new Random(); diff --git a/testData/results/pkg/TestClassVar.dec b/testData/results/pkg/TestClassVar.dec index 7756a767d7..475c7228c4 100644 --- a/testData/results/pkg/TestClassVar.dec +++ b/testData/results/pkg/TestClassVar.dec @@ -17,7 +17,7 @@ public class TestClassVar { }// 37 public Long testFieldSSAU1() { - return new Long((long)(this.field_int++));// 40 + return new Long(this.field_int++);// 40 } public void testComplexPropagation() { diff --git a/testData/results/pkg/TestDebugSymbols.dec b/testData/results/pkg/TestDebugSymbols.dec index 1300ffd36f..dcbc8997eb 100644 --- a/testData/results/pkg/TestDebugSymbols.dec +++ b/testData/results/pkg/TestDebugSymbols.dec @@ -5,7 +5,7 @@ class TestDebugSymbols { String text = "text";// 21 long prolonged = 42L;// 22 float decimated = (float)prolonged / 10.0F;// 23 - double doubled = (double)(2.0F * decimated);// 24 + double doubled = 2.0F * decimated;// 24 return (text + ":" + prolonged + ":" + decimated + ":" + doubled).length();// 25 } } diff --git a/testData/results/pkg/TestDefiniteAssignment.dec b/testData/results/pkg/TestDefiniteAssignment.dec index 614f039688..02fc48d261 100644 --- a/testData/results/pkg/TestDefiniteAssignment.dec +++ b/testData/results/pkg/TestDefiniteAssignment.dec @@ -56,9 +56,7 @@ public class TestDefiniteAssignment { double cFake = 0.01;// 48 System.out.println(cFake);// 49 - if ((double)n < 1.0 - (double)n && (cFake = (double)(n + 5)) > cFake * cFake - cFake / 2.0// 53 - ? !((double)n > cFake) - : !((double)n < 5.0 - (cFake = (double)n))) { + if (n < 1.0 - n && (cFake = n + 5) > cFake * cFake - cFake / 2.0 ? !(n > cFake) : !(n < 5.0 - (cFake = n))) {// 53 cFake += 5.0;// 57 } else { System.out.println(cFake);// 54 @@ -67,7 +65,7 @@ public class TestDefiniteAssignment { System.out.println(cFake);// 59 double d; - if ((d = (double)n) > 0.0) {// 63 + if ((d = n) > 0.0) {// 63 System.out.println(d);// 64 } }// 67 @@ -299,207 +297,207 @@ class 'pkg/TestDefiniteAssignment' { 67 58 68 58 69 58 - 6c 60 - 6d 60 - 6e 60 - 6f 60 - 70 60 - 71 60 - 72 60 - 74 60 - 75 60 - 76 60 - 77 60 - 78 60 - 7e 59 - 7f 59 - 80 59 - 81 59 - 82 59 - 83 59 - 86 63 - 87 63 - 88 63 - 89 63 - 8a 63 - 8b 63 - 8c 63 - 8d 63 - 90 64 - 91 64 - 92 64 - 94 64 - 95 64 - 9b 61 - 9c 61 - 9d 61 - 9f 61 - a0 61 - a1 67 - a2 67 - a3 67 - a4 67 - a5 67 - a6 67 - a7 67 - a8 67 - a9 69 - aa 69 - ac 69 - ad 69 - ae 69 - af 69 - b0 69 - bb 69 - be 70 - bf 70 - c0 70 - c1 70 - c2 70 - c3 70 - c6 72 + 6c 58 + 6d 58 + 6e 58 + 6f 58 + 70 58 + 71 58 + 72 58 + 74 58 + 75 58 + 76 58 + 77 58 + 78 58 + 7e 58 + 7f 58 + 80 58 + 81 58 + 82 58 + 83 58 + 86 61 + 87 61 + 88 61 + 89 61 + 8a 61 + 8b 61 + 8c 61 + 8d 61 + 90 62 + 91 62 + 92 62 + 94 62 + 95 62 + 9b 59 + 9c 59 + 9d 59 + 9f 59 + a0 59 + a1 65 + a2 65 + a3 65 + a4 65 + a5 65 + a6 65 + a7 65 + a8 65 + a9 67 + aa 67 + ac 67 + ad 67 + ae 67 + af 67 + b0 67 + bb 67 + be 68 + bf 68 + c0 68 + c1 68 + c2 68 + c3 68 + c6 70 } method 'testBooleanNormalness (I)V' { - 0 75 - 1 75 - 2 75 - 3 75 - 4 77 - 5 77 - 6 77 - 9 77 - a 77 - b 77 - d 77 - e 77 - 12 77 - 15 77 - 16 77 - 19 78 - 1a 78 - 1b 78 - 1c 78 - 1d 78 - 1e 78 - 21 81 - 22 81 - 23 81 - 24 82 - 25 82 - 26 82 - 27 82 - 28 82 - 29 82 - 2a 82 - 2b 83 + 0 73 + 1 73 + 2 73 + 3 73 + 4 75 + 5 75 + 6 75 + 9 75 + a 75 + b 75 + d 75 + e 75 + 12 75 + 15 75 + 16 75 + 19 76 + 1a 76 + 1b 76 + 1c 76 + 1d 76 + 1e 76 + 21 79 + 22 79 + 23 79 + 24 80 + 25 80 + 26 80 + 27 80 + 28 80 + 29 80 + 2a 80 + 2b 81 } method 'testBooleanNormalnessInline (I)V' { - 0 86 - 1 86 - 2 86 - 3 86 - 4 88 - 5 88 - 6 88 - 9 88 - a 88 - b 88 - d 88 - e 88 - 12 88 - 15 88 - 16 88 - 1e 88 - 1f 88 - 20 88 - 22 89 - 23 89 - 24 89 - 25 90 - 26 90 - 27 90 - 28 90 - 29 90 - 2a 90 - 2b 90 - 2c 91 + 0 84 + 1 84 + 2 84 + 3 84 + 4 86 + 5 86 + 6 86 + 9 86 + a 86 + b 86 + d 86 + e 86 + 12 86 + 15 86 + 16 86 + 1e 86 + 1f 86 + 20 86 + 22 87 + 23 87 + 24 87 + 25 88 + 26 88 + 27 88 + 28 88 + 29 88 + 2a 88 + 2b 88 + 2c 89 } method 'nestedTernaries (III)V' { - 0 96 - 1 96 - 4 97 - 5 97 - 6 97 - 9 101 - b 101 - c 101 - d 102 - 13 106 - 15 106 - 16 106 - 17 107 - 1a 107 - 1b 107 - 1c 107 - 1f 112 - 20 112 - 21 112 - 22 112 - 23 112 - 24 112 - 27 120 - 28 120 - 2b 121 - 2c 121 - 2d 121 - 30 125 - 32 125 - 33 125 - 34 126 - 3a 130 - 3c 130 - 3d 130 - 3e 131 - 41 131 - 42 131 - 43 131 - 46 136 - 47 136 - 48 136 - 49 136 - 4a 136 - 4d 136 - 4e 136 - 4f 136 - 51 136 - 52 136 - 53 136 - 54 136 - 5a 142 - 5c 142 - 5d 142 - 5e 143 - 60 143 - 61 143 - 62 144 - 65 149 - 66 149 - 67 149 - 68 149 - 69 149 - 6a 149 - 6d 152 - 6e 152 - 6f 152 - 70 152 - 71 152 - 72 152 - 73 152 - 74 152 - 75 153 + 0 94 + 1 94 + 4 95 + 5 95 + 6 95 + 9 99 + b 99 + c 99 + d 100 + 13 104 + 15 104 + 16 104 + 17 105 + 1a 105 + 1b 105 + 1c 105 + 1f 110 + 20 110 + 21 110 + 22 110 + 23 110 + 24 110 + 27 118 + 28 118 + 2b 119 + 2c 119 + 2d 119 + 30 123 + 32 123 + 33 123 + 34 124 + 3a 128 + 3c 128 + 3d 128 + 3e 129 + 41 129 + 42 129 + 43 129 + 46 134 + 47 134 + 48 134 + 49 134 + 4a 134 + 4d 134 + 4e 134 + 4f 134 + 51 134 + 52 134 + 53 134 + 54 134 + 5a 140 + 5c 140 + 5d 140 + 5e 141 + 60 141 + 61 141 + 62 142 + 65 147 + 66 147 + 67 147 + 68 147 + 69 147 + 6a 147 + 6d 150 + 6e 150 + 6f 150 + 70 150 + 71 150 + 72 150 + 73 150 + 74 150 + 75 151 } } @@ -526,27 +524,27 @@ Lines mapping: 48 <-> 57 49 <-> 58 53 <-> 59 -54 <-> 64 -55 <-> 65 -57 <-> 62 -59 <-> 68 -63 <-> 70 -64 <-> 71 -67 <-> 73 -71 <-> 76 -72 <-> 78 -73 <-> 79 -75 <-> 82 -76 <-> 83 -77 <-> 84 -81 <-> 87 -82 <-> 89 -83 <-> 90 -84 <-> 91 -85 <-> 92 -89 <-> 97 -90 <-> 113 -94 <-> 121 -97 <-> 150 -99 <-> 153 -100 <-> 154 +54 <-> 62 +55 <-> 63 +57 <-> 60 +59 <-> 66 +63 <-> 68 +64 <-> 69 +67 <-> 71 +71 <-> 74 +72 <-> 76 +73 <-> 77 +75 <-> 80 +76 <-> 81 +77 <-> 82 +81 <-> 85 +82 <-> 87 +83 <-> 88 +84 <-> 89 +85 <-> 90 +89 <-> 95 +90 <-> 111 +94 <-> 119 +97 <-> 148 +99 <-> 151 +100 <-> 152 diff --git a/testData/results/pkg/TestDoublePopAfterJump.dec b/testData/results/pkg/TestDoublePopAfterJump.dec index ee88d99f74..f08d915cdc 100644 --- a/testData/results/pkg/TestDoublePopAfterJump.dec +++ b/testData/results/pkg/TestDoublePopAfterJump.dec @@ -14,7 +14,7 @@ public final class TestDoublePopAfterJump { var10001 = var10004; } - return (double)var10001 / (double)var10000;// 44 45 46 + return (double)var10001 / var10000;// 44 45 46 } public static final void fizzBuzz() { diff --git a/testData/results/pkg/TestLocalsNames.dec b/testData/results/pkg/TestLocalsNames.dec index d54d30db1f..1a30a11df3 100644 --- a/testData/results/pkg/TestLocalsNames.dec +++ b/testData/results/pkg/TestLocalsNames.dec @@ -15,7 +15,7 @@ public class TestLocalsNames { } long elapsed = System.currentTimeMillis() - start;// 31 - System.out.println("took " + elapsed + "ms (" + elapsed / (long)files.length + "ms per dir)");// 32 + System.out.println("took " + elapsed + "ms (" + elapsed / files.length + "ms per dir)");// 32 } }// 34 } diff --git a/testData/results/pkg/TestMethodHandles.dec b/testData/results/pkg/TestMethodHandles.dec index 0484c05591..1706ff1f9f 100644 --- a/testData/results/pkg/TestMethodHandles.dec +++ b/testData/results/pkg/TestMethodHandles.dec @@ -12,33 +12,33 @@ public class TestMethodHandles { public void test1() throws Throwable { MethodHandle abs = LOOKUP.findStatic(Math.class, "abs", MethodType.methodType(long.class, long.class));// 10 int a = -5;// 11 - long b = (long)abs.invokeExact((long)a);// 12 + long b = (long)abs.invokeExact(a);// 12 System.out.println(b);// 13 }// 14 public int test2() throws Throwable { MethodHandle abs = LOOKUP.findStatic(Math.class, "abs", MethodType.methodType(long.class, long.class));// 17 int a = -5;// 18 - return (int)(long)abs.invokeExact((long)a);// 19 + return (int)(long)abs.invokeExact(a);// 19 } public void test3() throws Throwable { MethodHandle abs = LOOKUP.findStatic(Math.class, "abs", MethodType.methodType(long.class, long.class));// 23 int a = -5;// 24 - long b = (long)abs.invoke((long)a);// 25 + long b = (long)abs.invoke(a);// 25 System.out.println(b);// 26 }// 27 public int test4() throws Throwable { MethodHandle abs = LOOKUP.findStatic(Math.class, "abs", MethodType.methodType(long.class, long.class));// 30 int a = -5;// 31 - return (int)(long)abs.invoke((long)a);// 32 + return (int)(long)abs.invoke(a);// 32 } public void test5() throws Throwable { MethodHandle println = LOOKUP.findVirtual(PrintStream.class, "println", MethodType.methodType(void.class, long.class));// 36 int a = -5;// 37 - println.invokeExact(System.out, (long)a);// 38 + println.invokeExact(System.out, a);// 38 }// 39 } diff --git a/testData/results/pkg/TestMixedCompoundAssignment.dec b/testData/results/pkg/TestMixedCompoundAssignment.dec index 14008b4b08..dc21681687 100644 --- a/testData/results/pkg/TestMixedCompoundAssignment.dec +++ b/testData/results/pkg/TestMixedCompoundAssignment.dec @@ -2,53 +2,53 @@ package pkg; public class TestMixedCompoundAssignment { public int testSimpleIntFloat(int i, float j) { - i = (int)((float)i + j);// 5 - i = (int)((float)i - j);// 6 - i = (int)((float)i * j);// 7 - return (int)((float)i / j);// 8 10 + i = (int)(i + j);// 5 + i = (int)(i - j);// 6 + i = (int)(i * j);// 7 + return (int)(i / j);// 8 10 } public int testSimpleIntLong(int i, long j) { - i = (int)((long)i + j);// 14 - i = (int)((long)i - j);// 15 - i = (int)((long)i * j);// 16 - i = (int)((long)i / j);// 17 - i = (int)((long)i & j);// 18 - i = (int)((long)i | j);// 19 - i = (int)((long)i ^ j);// 20 + i = (int)(i + j);// 14 + i = (int)(i - j);// 15 + i = (int)(i * j);// 16 + i = (int)(i / j);// 17 + i = (int)(i & j);// 18 + i = (int)(i | j);// 19 + i = (int)(i ^ j);// 20 i >>= (int)j;// 21 i <<= (int)j;// 22 return i >>> (int)j;// 23 25 } public double testSimpleDoubleLong(double i, long j) { - i += (double)j;// 29 - i -= (double)j;// 30 - i *= (double)j;// 31 - return i / (double)j;// 32 34 + i += j;// 29 + i -= j;// 30 + i *= j;// 31 + return i / j;// 32 34 } public int testNestedIntLongDouble(int i, long j, double k) { long var9; - i = (int)((long)i + (var9 = (long)((double)j + k)));// 38 - i = (int)((long)i - (j = (long)((double)var9 - k)));// 39 + i = (int)(i + (var9 = (long)(j + k)));// 38 + i = (int)(i - (j = (long)(var9 - k)));// 39 long var11; - i = (int)((long)i * (var11 = (long)((double)j * k)));// 40 - return (int)((long)i / (long)((double)var11 / k));// 41 44 + i = (int)(i * (var11 = (long)(j * k)));// 40 + return (int)(i / (long)(var11 / k));// 41 44 } public long testNestedLongIntLong(long i, int j, long k) { int var15; - i += (long)(var15 = (int)((long)j + k));// 48 - i -= (long)(j = (int)((long)var15 - k));// 49 + i += var15 = (int)(j + k);// 48 + i -= j = (int)(var15 - k);// 49 int var17; - i *= (long)(var17 = (int)((long)j * k));// 50 - i /= (long)(j = (int)((long)var17 / k));// 51 + i *= var17 = (int)(j * k);// 50 + i /= j = (int)(var17 / k);// 51 int var19; - i &= (long)(var19 = (int)((long)j & k));// 52 - i |= (long)(j = (int)((long)var19 | k));// 53 + i &= var19 = (int)(j & k);// 52 + i |= j = (int)(var19 | k);// 53 int var21; - i ^= (long)(var21 = (int)((long)j ^ k));// 54 + i ^= var21 = (int)(j ^ k);// 54 i >>= j = var21 >> (int)k;// 55 int var23; i <<= var23 = j << (int)k;// 56 @@ -57,25 +57,25 @@ public class TestMixedCompoundAssignment { public void testArrayIntDouble(int[] holder, int i, double inc) { for (int j = 0; j < i; j++) {// 64 - holder[j] = (int)((double)holder[j] + inc);// 65 + holder[j] = (int)(holder[j] + inc);// 65 } }// 67 public void testArrayIntLong(int[] holder, int i, long inc) { for (int j = 0; j < i; j++) {// 70 - holder[j] = (int)((long)holder[j] + inc);// 71 + holder[j] = (int)(holder[j] + inc);// 71 } }// 73 public void testArrayDoubleInt(double[] holder, int i, int inc) { for (int j = 0; j < i; j++) {// 76 - holder[j] += (double)inc;// 77 + holder[j] += inc;// 77 } }// 79 public void testNestedArrayByteFloatLongDouble(byte[] outer, float[] holder, long[] inner, int i, double inc) { for (int j = 0; j < i; j++) {// 82 - outer[i + ~j] = (byte)((int)((float)outer[i + ~j] + (holder[j] -= (float)(inner[j * 3 % i] = (long)((double)inner[j * 3 % i] + inc)))));// 83 + outer[i + ~j] = (byte)(outer[i + ~j] + (holder[j] -= (float)(inner[j * 3 % i] = (long)(inner[j * 3 % i] + inc))));// 83 } }// 85 } diff --git a/testData/results/pkg/TestMultiCast.dec b/testData/results/pkg/TestMultiCast.dec index 42a0868055..37c4f4980f 100644 --- a/testData/results/pkg/TestMultiCast.dec +++ b/testData/results/pkg/TestMultiCast.dec @@ -2,7 +2,7 @@ package pkg; public class TestMultiCast { public void test(double arg1, double arg2) { - useNumbers((double)((int)arg1), (double)((int)arg2));// 5 + useNumbers((int)arg1, (int)arg2);// 5 }// 6 private static void useNumbers(double arg1, double arg2) { diff --git a/testData/results/pkg/TestMultipleStaticBlocks.dec b/testData/results/pkg/TestMultipleStaticBlocks.dec index 02242d41c0..c8fa547c75 100644 --- a/testData/results/pkg/TestMultipleStaticBlocks.dec +++ b/testData/results/pkg/TestMultipleStaticBlocks.dec @@ -4,12 +4,12 @@ public class TestMultipleStaticBlocks { private static int i; static { - byte value = (byte)((int)(Math.random() * 8.0));// 7 + byte value = (byte)(Math.random() * 8.0);// 7 if (value > 4) {// 8 i = 1;// 9 } - value = (byte)((short)((int)(Math.random() * 8.0)));// 14 + value = (byte)((short)(Math.random() * 8.0));// 14 if (value > 4) {// 15 i = 2;// 16 } diff --git a/testData/results/pkg/TestNumberCasts.dec b/testData/results/pkg/TestNumberCasts.dec new file mode 100644 index 0000000000..ed9dd15a90 --- /dev/null +++ b/testData/results/pkg/TestNumberCasts.dec @@ -0,0 +1,367 @@ +package pkg; + +public class TestNumberCasts { + private static void b(byte b) { + }// 5 + + private static void s(short s) { + }// 8 + + private static void i(int i) { + }// 11 + + private static void l(long l) { + }// 14 + + private static void f(float f) { + }// 17 + + private static void d(double d) { + }// 20 + + public void test() { + byte b = 127;// 23 + b(b);// 24 + s(b);// 25 + i(b);// 26 + l(b);// 27 + f(b);// 28 + d(b);// 29 + short s = 32767;// 31 + b((byte)s);// 32 + s(s);// 33 + i(s);// 34 + l(s);// 35 + f(s);// 36 + d(s);// 37 + int i = 2147483647;// 39 + b((byte)i);// 40 + s((short)i);// 41 + i(i);// 42 + l(i);// 43 + f(i);// 44 + d(i);// 45 + long l = 9223372036854775807L;// 47 + b((byte)l);// 48 + s((short)l);// 49 + i((int)l);// 50 + l(l);// 51 + f((float)l);// 52 + d(l);// 53 + float f = 3.4028235E38F;// 55 + b((byte)f);// 56 + s((short)f);// 57 + i((int)f);// 58 + l((long)f);// 59 + f(f);// 60 + d(f);// 61 + double d = 1.7976931348623157E308;// 63 + b((byte)d);// 64 + s((short)d);// 65 + i((int)d);// 66 + l((long)d);// 67 + f((float)d);// 68 + d(d);// 69 + }// 70 +} + +class 'pkg/TestNumberCasts' { + method 'b (B)V' { + 0 4 + } + + method 's (S)V' { + 0 7 + } + + method 'i (I)V' { + 0 10 + } + + method 'l (J)V' { + 0 13 + } + + method 'f (F)V' { + 0 16 + } + + method 'd (D)V' { + 0 19 + } + + method 'test ()V' { + 0 22 + 1 22 + 2 22 + 3 23 + 4 23 + 5 23 + 6 23 + 7 24 + 8 24 + 9 24 + a 24 + b 24 + c 25 + d 25 + e 25 + f 25 + 10 26 + 11 26 + 12 26 + 13 26 + 14 26 + 15 27 + 16 27 + 17 27 + 18 27 + 19 27 + 1a 28 + 1b 28 + 1c 28 + 1d 28 + 1e 28 + 1f 29 + 20 29 + 21 29 + 22 29 + 23 30 + 24 30 + 25 30 + 26 30 + 27 30 + 28 31 + 29 31 + 2a 31 + 2b 31 + 2c 32 + 2d 32 + 2e 32 + 2f 32 + 30 33 + 31 33 + 32 33 + 33 33 + 34 33 + 35 34 + 36 34 + 37 34 + 38 34 + 39 34 + 3a 35 + 3b 35 + 3c 35 + 3d 35 + 3e 35 + 3f 36 + 40 36 + 41 36 + 42 37 + 43 37 + 44 37 + 45 37 + 46 37 + 47 38 + 48 38 + 49 38 + 4a 38 + 4b 38 + 4c 39 + 4d 39 + 4e 39 + 4f 39 + 50 40 + 51 40 + 52 40 + 53 40 + 54 40 + 55 41 + 56 41 + 57 41 + 58 41 + 59 41 + 5a 42 + 5b 42 + 5c 42 + 5d 42 + 5e 42 + 5f 43 + 60 43 + 61 43 + 62 43 + 63 43 + 64 44 + 65 44 + 66 44 + 67 44 + 68 44 + 69 44 + 6a 44 + 6b 45 + 6c 45 + 6d 45 + 6e 45 + 6f 45 + 70 45 + 71 45 + 72 46 + 73 46 + 74 46 + 75 46 + 76 46 + 77 46 + 78 47 + 79 47 + 7a 47 + 7b 47 + 7c 47 + 7d 48 + 7e 48 + 7f 48 + 80 48 + 81 48 + 82 48 + 83 49 + 84 49 + 85 49 + 86 49 + 87 49 + 88 49 + 89 50 + 8a 50 + 8b 50 + 8c 50 + 8d 51 + 8e 51 + 8f 51 + 90 51 + 91 51 + 92 51 + 93 51 + 94 52 + 95 52 + 96 52 + 97 52 + 98 52 + 99 52 + 9a 52 + 9b 53 + 9c 53 + 9d 53 + 9e 53 + 9f 53 + a0 53 + a1 54 + a2 54 + a3 54 + a4 54 + a5 54 + a6 54 + a7 55 + a8 55 + a9 55 + aa 55 + ab 55 + ac 56 + ad 56 + ae 56 + af 56 + b0 56 + b1 56 + b2 57 + b3 57 + b4 57 + b5 57 + b6 57 + b7 58 + b8 58 + b9 58 + ba 58 + bb 58 + bc 58 + bd 58 + be 59 + bf 59 + c0 59 + c1 59 + c2 59 + c3 59 + c4 59 + c5 60 + c6 60 + c7 60 + c8 60 + c9 60 + ca 60 + cb 61 + cc 61 + cd 61 + ce 61 + cf 61 + d0 61 + d1 62 + d2 62 + d3 62 + d4 62 + d5 62 + d6 62 + d7 63 + d8 63 + d9 63 + da 63 + db 63 + dc 64 + } +} + +Lines mapping: +5 <-> 5 +8 <-> 8 +11 <-> 11 +14 <-> 14 +17 <-> 17 +20 <-> 20 +23 <-> 23 +24 <-> 24 +25 <-> 25 +26 <-> 26 +27 <-> 27 +28 <-> 28 +29 <-> 29 +31 <-> 30 +32 <-> 31 +33 <-> 32 +34 <-> 33 +35 <-> 34 +36 <-> 35 +37 <-> 36 +39 <-> 37 +40 <-> 38 +41 <-> 39 +42 <-> 40 +43 <-> 41 +44 <-> 42 +45 <-> 43 +47 <-> 44 +48 <-> 45 +49 <-> 46 +50 <-> 47 +51 <-> 48 +52 <-> 49 +53 <-> 50 +55 <-> 51 +56 <-> 52 +57 <-> 53 +58 <-> 54 +59 <-> 55 +60 <-> 56 +61 <-> 57 +63 <-> 58 +64 <-> 59 +65 <-> 60 +66 <-> 61 +67 <-> 62 +68 <-> 63 +69 <-> 64 +70 <-> 65 diff --git a/testData/results/pkg/TestNumberDisambiguation.dec b/testData/results/pkg/TestNumberDisambiguation.dec new file mode 100644 index 0000000000..26613185c1 --- /dev/null +++ b/testData/results/pkg/TestNumberDisambiguation.dec @@ -0,0 +1,384 @@ +package pkg; + +public class TestNumberDisambiguation { + public void foo(byte b) { + }// 5 + + public void foo(short s) { + }// 8 + + public void foo(char c) { + }// 11 + + public void foo(int i) { + }// 14 + + public void foo(long l) { + }// 17 + + public void foo(float f) { + }// 20 + + public void foo(double d) { + }// 23 + + public void bar(byte a, byte b) { + }// 26 + + public void bar(short a, short b) { + }// 29 + + public void bar(char a, char b) { + }// 32 + + public void bar(int a, int b) { + }// 35 + + public void bar(long a, long b) { + }// 38 + + public void bar(float a, float b) { + }// 41 + + public void bar(double a, double b) { + }// 44 + + public void baz(int a, byte b, byte c) { + }// 47 + + public void baz(int a, short b, short c) { + }// 50 + + public void baz(int a, char b, char c) { + }// 53 + + public void baz(int a, int b, int c) { + }// 56 + + public void baz(int a, long b, long c) { + }// 59 + + public void baz(int a, float b, float c) { + }// 62 + + public void baz(int a, double b, double c) { + }// 65 + + public void test() { + int i = 24;// 68 + this.foo((byte)i);// 69 + this.foo((short)i);// 70 + this.foo((char)i);// 71 + this.foo(i);// 72 + this.foo((long)i);// 73 + this.foo((float)i);// 74 + this.foo((double)i);// 75 + this.bar((byte)0, (byte)i);// 77 + this.bar((short)0, (short)i);// 78 + this.bar('\u0000', (char)i);// 79 + this.bar(0, i);// 80 + this.bar(0L, (long)i);// 81 + this.bar(0.0F, (float)i);// 82 + this.bar(0.0, (double)i);// 83 + this.baz(0, (byte)127, (byte)i);// 85 + this.baz(0, (short)32767, (short)i);// 86 + this.baz(0, '\uffff', (char)i);// 87 + this.baz(0, 2147483647, i);// 88 + this.baz(0, 9223372036854775807L, (long)i);// 89 + this.baz(0, 3.4028235E38F, (float)i);// 90 + this.baz(0, 1.7976931348623157E308, (double)i);// 91 + }// 92 +} + +class 'pkg/TestNumberDisambiguation' { + method 'foo (B)V' { + 0 4 + } + + method 'foo (S)V' { + 0 7 + } + + method 'foo (C)V' { + 0 10 + } + + method 'foo (I)V' { + 0 13 + } + + method 'foo (J)V' { + 0 16 + } + + method 'foo (F)V' { + 0 19 + } + + method 'foo (D)V' { + 0 22 + } + + method 'bar (BB)V' { + 0 25 + } + + method 'bar (SS)V' { + 0 28 + } + + method 'bar (CC)V' { + 0 31 + } + + method 'bar (II)V' { + 0 34 + } + + method 'bar (JJ)V' { + 0 37 + } + + method 'bar (FF)V' { + 0 40 + } + + method 'bar (DD)V' { + 0 43 + } + + method 'baz (IBB)V' { + 0 46 + } + + method 'baz (ISS)V' { + 0 49 + } + + method 'baz (ICC)V' { + 0 52 + } + + method 'baz (III)V' { + 0 55 + } + + method 'baz (IJJ)V' { + 0 58 + } + + method 'baz (IFF)V' { + 0 61 + } + + method 'baz (IDD)V' { + 0 64 + } + + method 'test ()V' { + 0 67 + 1 67 + 2 67 + 3 68 + 4 68 + 5 68 + 6 68 + 7 68 + 8 68 + 9 69 + a 69 + b 69 + c 69 + d 69 + e 69 + f 70 + 10 70 + 11 70 + 12 70 + 13 70 + 14 70 + 15 71 + 16 71 + 17 71 + 18 71 + 19 71 + 1a 72 + 1b 72 + 1c 72 + 1d 72 + 1e 72 + 1f 72 + 20 73 + 21 73 + 22 73 + 23 73 + 24 73 + 25 73 + 26 74 + 27 74 + 28 74 + 29 74 + 2a 74 + 2b 74 + 2c 75 + 2d 75 + 2e 75 + 2f 75 + 30 75 + 31 75 + 32 75 + 33 76 + 34 76 + 35 76 + 36 76 + 37 76 + 38 76 + 39 76 + 3a 77 + 3b 77 + 3c 77 + 3d 77 + 3e 77 + 3f 77 + 40 77 + 41 78 + 42 78 + 43 78 + 44 78 + 45 78 + 46 78 + 47 79 + 48 79 + 49 79 + 4a 79 + 4b 79 + 4c 79 + 4d 79 + 4e 80 + 4f 80 + 50 80 + 51 80 + 52 80 + 53 80 + 54 80 + 55 81 + 56 81 + 57 81 + 58 81 + 59 81 + 5a 81 + 5b 81 + 5c 82 + 5d 82 + 5e 82 + 5f 82 + 60 82 + 61 82 + 62 82 + 63 82 + 64 82 + 65 83 + 66 83 + 67 83 + 68 83 + 69 83 + 6a 83 + 6b 83 + 6c 83 + 6d 83 + 6e 83 + 6f 84 + 70 84 + 71 84 + 72 84 + 73 84 + 74 84 + 75 84 + 76 84 + 77 84 + 78 85 + 79 85 + 7a 85 + 7b 85 + 7c 85 + 7d 85 + 7e 85 + 7f 85 + 80 86 + 81 86 + 82 86 + 83 86 + 84 86 + 85 86 + 86 86 + 87 86 + 88 86 + 89 86 + 8a 87 + 8b 87 + 8c 87 + 8d 87 + 8e 87 + 8f 87 + 90 87 + 91 87 + 92 87 + 93 88 + 94 88 + 95 88 + 96 88 + 97 88 + 98 88 + 99 88 + 9a 88 + 9b 88 + 9c 88 + 9d 89 + } +} + +Lines mapping: +5 <-> 5 +8 <-> 8 +11 <-> 11 +14 <-> 14 +17 <-> 17 +20 <-> 20 +23 <-> 23 +26 <-> 26 +29 <-> 29 +32 <-> 32 +35 <-> 35 +38 <-> 38 +41 <-> 41 +44 <-> 44 +47 <-> 47 +50 <-> 50 +53 <-> 53 +56 <-> 56 +59 <-> 59 +62 <-> 62 +65 <-> 65 +68 <-> 68 +69 <-> 69 +70 <-> 70 +71 <-> 71 +72 <-> 72 +73 <-> 73 +74 <-> 74 +75 <-> 75 +77 <-> 76 +78 <-> 77 +79 <-> 78 +80 <-> 79 +81 <-> 80 +82 <-> 81 +83 <-> 82 +85 <-> 83 +86 <-> 84 +87 <-> 85 +88 <-> 86 +89 <-> 87 +90 <-> 88 +91 <-> 89 +92 <-> 90 diff --git a/testData/results/pkg/TestObjectBitwise.dec b/testData/results/pkg/TestObjectBitwise.dec index 9792f57f5b..82ce7049c0 100644 --- a/testData/results/pkg/TestObjectBitwise.dec +++ b/testData/results/pkg/TestObjectBitwise.dec @@ -4,27 +4,27 @@ public abstract class TestObjectBitwise { abstract T get(); public boolean test(int i) { - return ((Long)this.obj() & (long)i) == 0L;// 7 + return ((Long)this.obj() & i) == 0L;// 7 } public boolean testn(int i) { - return ((Long)this.num() & (long)i) == 0L;// 11 + return ((Long)this.num() & i) == 0L;// 11 } public boolean testg(int i) { - return ((Long)this.get() & (long)i) == 0L;// 15 + return ((Long)this.get() & i) == 0L;// 15 } public boolean test1(int i) { - return ((Long)this.obj() | (long)i) == 0L;// 50 + return ((Long)this.obj() | i) == 0L;// 50 } public boolean test3(int i) { - return (Long)this.obj() + (long)i == 0L;// 54 + return (Long)this.obj() + i == 0L;// 54 } public boolean test4(int i) { - return (Long)this.obj() % (long)i == 0L;// 58 + return (Long)this.obj() % i == 0L;// 58 } public Object obj() { @@ -43,17 +43,17 @@ public abstract class TestObjectBitwise { } public boolean testg_inner(int i) { - return (this.get() & (long)i) == 0L;// 27 + return (this.get() & i) == 0L;// 27 } public boolean testg_inner2(int i) { long l = this.get();// 31 - return (l & (long)i) == 0L;// 32 + return (l & i) == 0L;// 32 } public boolean testg_inner3(int i) { long l = this.other.get();// 36 - return (l & (long)i) == 0L;// 37 + return (l & i) == 0L;// 37 } public boolean testg_inner4(int i) { @@ -61,7 +61,7 @@ public abstract class TestObjectBitwise { long l2 = (Long)this.other.num();// 42 long l3 = (Long)this.obj();// 43 long l4 = (Long)this.num();// 44 - return (l & (long)i & l2 & l3 & l4) == 0L;// 45 + return (l & i & l2 & l3 & l4) == 0L;// 45 } } } diff --git a/testData/results/pkg/TestRecordPattern3.dec b/testData/results/pkg/TestRecordPattern3.dec index 529e04b395..ba8bbcb6c3 100644 --- a/testData/results/pkg/TestRecordPattern3.dec +++ b/testData/results/pkg/TestRecordPattern3.dec @@ -27,7 +27,7 @@ public class TestRecordPattern3 { double dd = Double.valueOf(var32); boolean var33 = $proxy$bool(bundle); boolean bool = Boolean.valueOf(var33); - System.out.println(a + var21 + var22 + ((double)((long)(i1 + i2 + i3 + i4) + l1 + l2 + (long)c1 + (long)c2 + (long)bb) + dd) + bool);// 11 + System.out.println(a + var21 + var22 + (i1 + i2 + i3 + i4 + l1 + l2 + c1 + c2 + bb + dd) + bool);// 11 } }// 13 diff --git a/testData/results/pkg/TestStackCastParam.dec b/testData/results/pkg/TestStackCastParam.dec index cad0c3f392..2d76de1e67 100644 --- a/testData/results/pkg/TestStackCastParam.dec +++ b/testData/results/pkg/TestStackCastParam.dec @@ -5,8 +5,8 @@ public class TestStackCastParam { public int y; public void test(String s) { - float var10002 = (float)this.x; - get().b.accept(s, var10002, (float)this.y);// 8 + float var10002 = this.x; + get().b.accept(s, var10002, this.y);// 8 }// 9 public static TestStackCastParam.A get() { diff --git a/testData/results/pkg/TestWhileTernary10.dec b/testData/results/pkg/TestWhileTernary10.dec index 5d4ec6f136..b3ef2d2a07 100644 --- a/testData/results/pkg/TestWhileTernary10.dec +++ b/testData/results/pkg/TestWhileTernary10.dec @@ -4,7 +4,7 @@ import java.util.stream.Stream; public class TestWhileTernary10 { public double test(boolean condition, int n, Stream doubles) { - double[] ds = new double[]{(double)n};// 7 + double[] ds = new double[]{n};// 7 for (int i = 0; condition ? i >= n : n >= i; i++) {// 9 for (int j = 0; j < n; j++) {// 10 @@ -20,10 +20,10 @@ public class TestWhileTernary10 { } public double test1(boolean condition, int n, Stream doubles) { - double[] ds = new double[]{(double)n};// 24 + double[] ds = new double[]{n};// 24 for (int i = 0; condition ? i >= n : n >= i; i++) {// 26 - ds[0] += (double)i;// 27 + ds[0] += i;// 27 } doubles.forEach(d -> ds[0] -= d);// 30 @@ -31,7 +31,7 @@ public class TestWhileTernary10 { } public double test2(boolean condition, int n, Stream doubles) { - double[] ds = new double[]{(double)n};// 35 + double[] ds = new double[]{n};// 35 for (int i = 0; condition ? i >= n : n >= i; i++) {// 37 for (int j = 0; j < n; j++) {// 38 diff --git a/testData/src/java8/pkg/TestNumberCasts.java b/testData/src/java8/pkg/TestNumberCasts.java new file mode 100644 index 0000000000..0b111c4504 --- /dev/null +++ b/testData/src/java8/pkg/TestNumberCasts.java @@ -0,0 +1,71 @@ +package pkg; + +public class TestNumberCasts { + private static void b(byte b) { + } + + private static void s(short s) { + } + + private static void i(int i) { + } + + private static void l(long l) { + } + + private static void f(float f) { + } + + private static void d(double d) { + } + + public void test() { + byte b = 127; + b(b); + s(b); + i(b); + l(b); + f(b); + d(b); + + short s = 32767; + b((byte) s); + s(s); + i(s); + l(s); + f(s); + d(s); + + int i = 2147483647; + b((byte) i); + s((short) i); + i(i); + l(i); + f(i); + d(i); + + long l = 9223372036854775807L; + b((byte) l); + s((short) l); + i((int) l); + l(l); + f(l); + d(l); + + float f = 3.4028235E38f; + b((byte) f); + s((short) f); + i((int) f); + l((long) f); + f(f); + d(f); + + double d = 1.7976931348623157E308; + b((byte) d); + s((short) d); + i((int) d); + l((long) d); + f((float) d); + d(d); + } +} diff --git a/testData/src/java8/pkg/TestNumberDisambiguation.java b/testData/src/java8/pkg/TestNumberDisambiguation.java new file mode 100644 index 0000000000..632432551d --- /dev/null +++ b/testData/src/java8/pkg/TestNumberDisambiguation.java @@ -0,0 +1,93 @@ +package pkg; + +public class TestNumberDisambiguation { + public void foo(byte b) { + } + + public void foo(short s) { + } + + public void foo(char c) { + } + + public void foo(int i) { + } + + public void foo(long l) { + } + + public void foo(float f) { + } + + public void foo(double d) { + } + + public void bar(byte a, byte b) { + } + + public void bar(short a, short b) { + } + + public void bar(char a, char b) { + } + + public void bar(int a, int b) { + } + + public void bar(long a, long b) { + } + + public void bar(float a, float b) { + } + + public void bar(double a, double b) { + } + + public void baz(int a, byte b, byte c) { + } + + public void baz(int a, short b, short c) { + } + + public void baz(int a, char b, char c) { + } + + public void baz(int a, int b, int c) { + } + + public void baz(int a, long b, long c) { + } + + public void baz(int a, float b, float c) { + } + + public void baz(int a, double b, double c) { + } + + public void test() { + int i = 24; + foo((byte) i); + foo((short) i); + foo((char) i); + foo(i); + foo((long) i); + foo((float) i); + foo((double) i); + + bar((byte) 0, (byte) i); + bar((short) 0, (short) i); + bar('\u0000', (char) i); + bar(0, i); + bar(0L, i); + bar(0.0F, i); + bar(0.0, i); + + baz(0, (byte) 127, (byte) i); + baz(0, (short) 32767, (short) i); + baz(0, '\uFFFF', (char) i); + baz(0, Integer.MAX_VALUE, i); + baz(0, Long.MAX_VALUE, i); + baz(0, Float.MAX_VALUE, i); + baz(0, Double.MAX_VALUE, i); + } +} From 4c5ebf8ece2053265c05644ae38504f1912b77ed Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:48:52 -0400 Subject: [PATCH 15/17] Move from a fixed thread pool to a fork join pool --- .../java/decompiler/struct/ContextUnit.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index 5ddb6029ee..b1fd3b753b 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -18,10 +18,12 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; public class ContextUnit { + private static AtomicInteger THREAD_ID = new AtomicInteger(0); private final IContextSource source; private final boolean own; private final boolean root; @@ -140,9 +142,12 @@ public void save(final Function loader) throws IOException } //Whooo threads! - final List> futures = new LinkedList<>(); - final int threads = Integer.parseInt((String) DecompilerContext.getProperty(IFernflowerPreferences.THREADS)); - final ExecutorService workerExec = Executors.newFixedThreadPool(threads > 0 ? threads : Runtime.getRuntime().availableProcessors()); + List> futures = new ArrayList<>(); + int threads = Integer.parseInt((String) DecompilerContext.getProperty(IFernflowerPreferences.THREADS)); + if (threads <= 0) { + threads = Runtime.getRuntime().availableProcessors(); + } + ForkJoinPool pool = new ForkJoinPool(threads, namingScheme(), null, true); final DecompilerContext rootContext = DecompilerContext.getCurrentContext(); final List toDump = new ArrayList<>(classEntries.size()); @@ -157,7 +162,7 @@ public void save(final Function loader) throws IOException // pre-process for (final ClassContext classCtx : toDump) { - futures.add(workerExec.submit(() -> { + futures.add(pool.submit(() -> { setContext(rootContext); classCtx.ctx = DecompilerContext.getCurrentContext(); try { @@ -182,7 +187,7 @@ public void save(final Function loader) throws IOException continue; } - futures.add(workerExec.submit(() -> { + futures.add(pool.submit(() -> { DecompilerContext.setCurrentContext(classCtx.ctx); classCtx.classContent = decompiledData.getClassContent(classCtx.cl); if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { @@ -193,7 +198,8 @@ public void save(final Function loader) throws IOException waitForAll(futures); futures.clear(); - workerExec.shutdown(); + pool.shutdown(); + THREAD_ID.set(0); // write to file for (final ClassContext cls : toDump) { @@ -205,6 +211,15 @@ public void save(final Function loader) throws IOException sink.close(); } + private static ForkJoinPool.ForkJoinWorkerThreadFactory namingScheme() { + return pool -> { + ForkJoinWorkerThread thread = new ForkJoinWorkerThread(pool) {}; + thread.setName("Vineflower-DecompilerThread-" + THREAD_ID.getAndIncrement()); + + return thread; + }; + } + public void setContext(DecompilerContext rootContext) { DecompilerContext current = DecompilerContext.getCurrentContext(); if (current == null) { @@ -221,7 +236,9 @@ public void setContext(DecompilerContext rootContext) { } private static void waitForAll(final List> futures) { - for (Future future : futures) { + for (int i = futures.size() - 1; i >= 0; i--) { + Future future = futures.get(i); + try { future.get(); } catch (ExecutionException e) { From ba3328a2c5cf28868e0668014ff882011bd1d4ed Mon Sep 17 00:00:00 2001 From: Muhamad Visat <9150540+mvisat@users.noreply.github.com> Date: Sun, 14 Apr 2024 10:57:08 +0800 Subject: [PATCH 16/17] Add option to exclude classes from decompilation with regex (#370) * add option to exclude classes from decompilation * add @Type annotation * rename into excluded-classes, remove short name --- .../java/decompiler/main/ClassesProcessor.java | 11 +++++++++++ .../main/extern/IFernflowerPreferences.java | 10 ++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index a5c5c5818b..1fc3c0a1f1 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -36,6 +36,8 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class ClassesProcessor implements CodeConstants { public static final int AVERAGE_CLASS_SIZE = 16 * 1024; @@ -94,8 +96,17 @@ public void loadClasses(IIdentifierRenamer renamer) { boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER); boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES); + Matcher excludedMatcher = null; + String excludedRegex = DecompilerContext.getProperty(IFernflowerPreferences.EXCLUDED_CLASSES).toString(); + if (!excludedRegex.isEmpty()) { + excludedMatcher = Pattern.compile(excludedRegex).matcher(""); + } + // create class nodes for (StructClass cl : context.getOwnClasses()) { + if (excludedMatcher != null && excludedMatcher.reset(cl.qualifiedName).matches()) { + continue; + } if (!mapRootClasses.containsKey(cl.qualifiedName)) { if (bDecompileInner) { StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES); diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index b2f4b2acd0..e674a7c31b 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -382,6 +382,11 @@ public interface IFernflowerPreferences { @ShortName("mcs") String MARK_CORRESPONDING_SYNTHETICS = "mark-corresponding-synthetics"; + @Name("Excluded Classes") + @Description("Exclude classes from decompilation if their fully qualified names match the specified regular expression.") + @Type(Type.STRING) + String EXCLUDED_CLASSES = "excluded-classes"; + Map DEFAULTS = getDefaults(); static Map getDefaults() { @@ -452,6 +457,7 @@ static Map getDefaults() { defaults.put(DUMP_TEXT_TOKENS, "0"); defaults.put(REMOVE_IMPORTS, "0"); defaults.put(MARK_CORRESPONDING_SYNTHETICS, "0"); + defaults.put(EXCLUDED_CLASSES, ""); return Collections.unmodifiableMap(defaults); } @@ -475,7 +481,7 @@ static Map getDefaults() { public @interface Description { String value(); } - + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DynamicDefaultValue { @@ -504,7 +510,7 @@ static Map getDefaults() { @Target(ElementType.FIELD) public @interface Type { String value(); - + String BOOLEAN = "bool"; String INTEGER = "int"; String STRING = "string"; From de78fe51607e7413a5361ebaaf41fb727166e127 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat, 13 Apr 2024 22:25:52 -0700 Subject: [PATCH 17/17] Expose lvt LocalVariable to renaming plugins, add simple renamer that fixes conflicting lvt names --- .../variablerenaming/JADNameProvider.java | 6 +- .../variablerenaming/SimpleNameProvider.java | 93 +++++++++++++++++++ .../variablerenaming/TinyNameProvider.java | 9 +- .../VariableRenamingPlugin.java | 1 + .../main/IdentityRenamerFactory.java | 4 +- .../main/extern/IVariableNameProvider.java | 28 +++++- .../decompiler/vars/VarDefinitionHelper.java | 15 ++- 7 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/SimpleNameProvider.java diff --git a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/JADNameProvider.java b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/JADNameProvider.java index 408e31b3d1..2d5c3b100b 100644 --- a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/JADNameProvider.java +++ b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/JADNameProvider.java @@ -27,7 +27,6 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -35,7 +34,6 @@ import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.Pair; public class JADNameProvider implements IVariableNameProvider { private HashMap last; @@ -103,7 +101,7 @@ public Holder(int t1, boolean skip_zero, List names) { } @Override - public Map rename(Map> entries) { + public Map renameVariables(Map entries) { int params = 0; if ((this.method.getAccessFlags() & CodeConstants.ACC_STATIC) != CodeConstants.ACC_STATIC) { params++; @@ -119,7 +117,7 @@ public Map rename(Map result = new LinkedHashMap<>(); for (VarVersionPair ver : keys) { - String type = cleanType(entries.get(ver).b); + String type = cleanType(entries.get(ver).typeName()); if ("this".equals(type)) { continue; } diff --git a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/SimpleNameProvider.java b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/SimpleNameProvider.java new file mode 100644 index 0000000000..3bd73da6ab --- /dev/null +++ b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/SimpleNameProvider.java @@ -0,0 +1,93 @@ +package org.vineflower.variablerenaming; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; +import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class SimpleNameProvider implements IVariableNameProvider { + private final boolean renameParameters; + private final StructMethod method; + private final Map usedNames = new HashMap<>(); + private final Map parameters = new HashMap<>(); + + public SimpleNameProvider(boolean renameParameters, StructMethod method) { + this.renameParameters = renameParameters; + this.method = method; + } + + @Override + public Map renameVariables(Map entries) { + int params = 0; + if ((this.method.getAccessFlags() & CodeConstants.ACC_STATIC) != CodeConstants.ACC_STATIC) { + params++; + } + + MethodDescriptor md = MethodDescriptor.parseDescriptor(this.method.getDescriptor()); + for (VarType param : md.params) { + params += param.stackSize; + } + + List keys = new ArrayList<>(entries.keySet()); + Collections.sort(keys, (o1, o2) -> (o1.var != o2.var) ? o1.var - o2.var : o1.version - o2.version); + + Map result = new LinkedHashMap<>(); + for (VarVersionPair ver : keys) { + String origName = entries.get(ver).lvt().getName(); + if (origName == null) { + origName = "lv"; + } + final String origNameFinal = origName; + if (ver.var >= params) { + this.method.getLocalVariableAttr().getMapNames(); + result.put(ver, getNewName(origNameFinal)); + } else if (renameParameters) { + result.put(ver, this.parameters.computeIfAbsent(ver.var, k -> getNewName(origNameFinal))); + } + } + + return result; + } + + private String getNewName(String name) { + int timesUsed = this.usedNames.compute(name, (k, v) -> v == null ? 1 : v + 1); + if (timesUsed == 1) { + return name; + } + return name + (timesUsed - 2); + } + + @Override + public String renameParameter(int flags, VarType type, String name, int index) { + if (!this.renameParameters) { + return IVariableNameProvider.super.renameParameter(flags, type, name, index); + } + return this.parameters.computeIfAbsent(index, k -> getNewName(name)); + } + + @Override + public void addParentContext(IVariableNameProvider renamer) { + if (renamer instanceof SimpleNameProvider s) { + s.usedNames.forEach((k, v) -> this.usedNames.merge(k, v, Integer::sum)); + } + } + + public static class SimpleNameProviderFactory implements IVariableNamingFactory { + + @Override + public IVariableNameProvider createFactory(StructMethod structMethod) { + return new SimpleNameProvider( + DecompilerContext.getOption(VariableRenamingOptions.RENAME_PARAMETERS), structMethod); + } + } +} diff --git a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/TinyNameProvider.java b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/TinyNameProvider.java index dd59747082..3d7f464ffe 100644 --- a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/TinyNameProvider.java +++ b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/TinyNameProvider.java @@ -2,7 +2,6 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.collectors.ImportCollector; import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; @@ -29,7 +28,7 @@ public TinyNameProvider(boolean renameParameters, StructMethod method) { } @Override - public Map rename(Map> entries) { + public Map renameVariables(Map entries) { int params = 0; if ((this.method.getAccessFlags() & CodeConstants.ACC_STATIC) != CodeConstants.ACC_STATIC) { params++; @@ -45,12 +44,12 @@ public Map rename(Map result = new LinkedHashMap<>(); for (VarVersionPair ver : keys) { - String type = cleanType(entries.get(ver).b); + String type = cleanType(entries.get(ver).typeName()); if (ver.var >= params) { - result.put(ver, getNewName(Pair.of(entries.get(ver).a, type))); + result.put(ver, getNewName(Pair.of(entries.get(ver).type(), type))); } else if (renameParameters) { - result.put(ver, this.parameters.computeIfAbsent(ver.var, k -> getNewName(Pair.of(entries.get(ver).a, type)))); + result.put(ver, this.parameters.computeIfAbsent(ver.var, k -> getNewName(Pair.of(entries.get(ver).type(), type)))); } } diff --git a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/VariableRenamingPlugin.java b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/VariableRenamingPlugin.java index 185d44990f..954b809af7 100644 --- a/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/VariableRenamingPlugin.java +++ b/plugins/variable-renaming/src/main/java/org/vineflower/variablerenaming/VariableRenamingPlugin.java @@ -41,5 +41,6 @@ public String description() { static { Renamers.registerProvider("jad", new JADNameProvider.JADNameProviderFactory()); Renamers.registerProvider("tiny", new TinyNameProvider.TinyNameProviderFactory()); + Renamers.registerProvider("fix_clashing_lvt", new SimpleNameProvider.SimpleNameProviderFactory()); } } diff --git a/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java b/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java index d49b0ed8ff..adc2aeb8fb 100644 --- a/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java +++ b/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java @@ -21,8 +21,6 @@ import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; import org.jetbrains.java.decompiler.struct.StructMethod; -import org.jetbrains.java.decompiler.struct.gen.VarType; -import org.jetbrains.java.decompiler.util.Pair; public class IdentityRenamerFactory implements IVariableNamingFactory, IVariableNameProvider { @Override @@ -31,7 +29,7 @@ public IVariableNameProvider createFactory(StructMethod method) { } @Override - public Map rename(Map> variables) { + public Map renameVariables(Map variables) { return null; } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java index d0aee739ba..0aa8c8a053 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java @@ -17,13 +17,38 @@ import java.util.Map; +import java.util.stream.Collectors; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.Pair; public interface IVariableNameProvider { - Map rename(Map> variables); + interface VariableNamingData { + VarType type(); + String typeName(); + StructLocalVariableTableAttribute.LocalVariable lvt(); + } + + default Map renameVariables(Map variables) { + return this.rename(variables.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> Pair.of(e.getValue().type(), e.getValue().typeName()) + ))); + } + + /** + * Renames the variables. + * + * @param variables variables + * @return map of variable pairs to new names + * @deprecated implement {@link #renameVariables(Map)} instead. this method exists for backwards ABI compatibility + */ + @Deprecated + default Map rename(Map> variables) { + throw new UnsupportedOperationException(); + } default String renameAbstractParameter(String name, int index) { return name; @@ -36,5 +61,6 @@ default String renameParameter(int flags, VarType type, String name, int index) return name; } + public void addParentContext(IVariableNameProvider renamer); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 97fc30c16f..4e94606896 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -6,6 +6,7 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.ValidationHelper; @@ -17,6 +18,7 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute.LocalVariable; import org.jetbrains.java.decompiler.struct.attr.StructMethodParametersAttribute; import org.jetbrains.java.decompiler.struct.gen.CodeType; @@ -26,7 +28,6 @@ import org.jetbrains.java.decompiler.struct.gen.generics.GenericType; import org.jetbrains.java.decompiler.util.ArrayHelper; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import org.jetbrains.java.decompiler.util.Pair; import org.jetbrains.java.decompiler.util.StatementIterator; import java.util.*; @@ -997,6 +998,12 @@ private VarType getMergedType(VarType fromMin, VarType toMin, VarType fromMax, V } } + record VariableNamingDataImpl( + VarType type, + String typeName, + StructLocalVariableTableAttribute.LocalVariable lvt + ) implements IVariableNameProvider.VariableNamingData {} + private void propogateLVTs(Statement stat) { MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); Map types = new LinkedHashMap<>(); @@ -1022,12 +1029,12 @@ private void propogateLVTs(Statement stat) { findTypes(stat, types); - Map> typeNames = new LinkedHashMap<>(); + Map typeNames = new LinkedHashMap<>(); for (Entry e : types.entrySet()) { - typeNames.put(e.getKey(), Pair.of(e.getValue().getType(), e.getValue().getCast())); + typeNames.put(e.getKey(), new VariableNamingDataImpl(e.getValue().getType(), e.getValue().getCast(), e.getValue().getLVT())); } - Map renames = this.mt.getVariableNamer().rename(typeNames); + Map renames = this.mt.getVariableNamer().renameVariables(typeNames); Set methods = new HashSet<>();