From 31cff62c9494a0527d89dfe4d2fcb0c9f6f095ea Mon Sep 17 00:00:00 2001 From: Maxim Degtyarev Date: Thu, 15 Apr 2021 19:37:55 +0300 Subject: [PATCH] Fix for IDEABKL-8006 IDE hangs when decompiling class which is its own superclass GitOrigin-RevId: 1fe14694ce69b135f2e3fe4cde84ce3d42997228 --- .../main/collectors/ImportCollector.java | 17 ++++++++--- .../java/decompiler/SingleClassesTest.java | 11 +++++++ .../pkg/TestInheritanceChainCycle.class | Bin 0 -> 458 bytes .../results/TestInheritanceChainCycle.dec | 22 ++++++++++++++ .../src/pkg/TestInheritanceChainCycle.jasm | 27 ++++++++++++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 testData/classes/pkg/TestInheritanceChainCycle.class create mode 100644 testData/results/TestInheritanceChainCycle.dec create mode 100644 testData/src/pkg/TestInheritanceChainCycle.jasm diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index 647ac7c..dc6b3a5 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -39,8 +39,10 @@ public class ImportCollector { Map classes = DecompilerContext.getStructContext().getClasses(); LinkedList queue = new LinkedList<>(); + Set processedClasses = new HashSet<>(); StructClass currentClass = root.classStruct; while (currentClass != null) { + processedClasses.add(currentClass); if (currentClass.superClass != null) { queue.add(currentClass.superClass.getString()); } @@ -63,10 +65,17 @@ public class ImportCollector { } // .. and traverse through parent. - currentClass = !queue.isEmpty() ? classes.get(queue.removeFirst()) : null; - while (currentClass == null && !queue.isEmpty()) { - currentClass = classes.get(queue.removeFirst()); - } + do { + currentClass = queue.isEmpty() ? null : classes.get(queue.removeFirst()); + + if (currentClass != null && processedClasses.contains(currentClass)) { + // Class already processed, skipping. + + // This may be sign of circularity in the class hierarchy but in most cases this mean that same interface + // are listed as implemented several times in the class hierarchy. + currentClass = null; + } + } while (currentClass == null && !queue.isEmpty()); } } diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index feea5aa..e0a9b6f 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -5,7 +5,9 @@ import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import java.io.File; import java.io.IOException; @@ -19,6 +21,13 @@ import static org.junit.Assert.assertTrue; public class SingleClassesTest { private DecompilerTestFixture fixture; + /* + * Set individual test duration time limit to 60 seconds. + * This will help us to test bugs hanging decompiler. + */ + @Rule + public Timeout globalTimeout = Timeout.seconds(60); + @Before public void setUp() throws IOException { fixture = new DecompilerTestFixture(); @@ -135,6 +144,8 @@ public class SingleClassesTest { @Test public void testRecordGenericVararg() { doTest("records/TestRecordGenericVararg"); } @Test public void testRecordAnno() { doTest("records/TestRecordAnno"); } + @Test public void testInheritanceChainCycle() { doTest("pkg/TestInheritanceChainCycle"); } + private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); diff --git a/testData/classes/pkg/TestInheritanceChainCycle.class b/testData/classes/pkg/TestInheritanceChainCycle.class new file mode 100644 index 0000000000000000000000000000000000000000..0969e39ad78fea01e575e2aa749252540730efe9 GIT binary patch literal 458 zcmZ{f%TB^T6o&udRwxw^;|}A7#At$-1j2rblQP&&Njo*+v2>w{3m?FT zG9GFoVc|@2&di+ifB%`U?~hLa=h(}mUO*EC69zU+6tT-t?+j-AncE&$u07PDRBo?3 zW=IFIHjrnitjhP!OiZ+e9BK>)BN6EvH5H-MTunrO%BAWrCZ0H(@yIt|G1L;R4D1Ku zYN8A7Ti8K`!5jv0I1yLU6AZ?DS@#s#cDiE&RfhfflWYHLL396(z0U42WctA)5sTNt z^MaE@CwAKm<$+Y4HuCXK7*CaaVw&Ty9CKyNG0{9g)*{eW0UrB zrNEYdptwc1Bg%|&_V^v?7dkLBD3*{;BAFwTG;jY&P7?%Bctxtp-jG?zs?-eBN!%dM NCh}+zutaYum_Ml&V1obv literal 0 HcmV?d00001 diff --git a/testData/results/TestInheritanceChainCycle.dec b/testData/results/TestInheritanceChainCycle.dec new file mode 100644 index 0000000..f2490af --- /dev/null +++ b/testData/results/TestInheritanceChainCycle.dec @@ -0,0 +1,22 @@ +package pkg; + +public class TestInheritanceChainCycle extends TestInheritanceChainCycle { + public void printMessage() { + System.out.println("Hello, bug!");// 21 22 23 + }// 24 +} + +class 'pkg/TestInheritanceChainCycle' { + method 'printMessage ()V' { + 0 4 + 3 4 + 5 4 + 8 5 + } +} + +Lines mapping: +21 <-> 5 +22 <-> 5 +23 <-> 5 +24 <-> 6 diff --git a/testData/src/pkg/TestInheritanceChainCycle.jasm b/testData/src/pkg/TestInheritanceChainCycle.jasm new file mode 100644 index 0000000..b6ccdb6 --- /dev/null +++ b/testData/src/pkg/TestInheritanceChainCycle.jasm @@ -0,0 +1,27 @@ +/** + * This code can be assembled with asmtools + * using asmtools jasm -g *.jasm command line. + */ +package pkg; + +super public class TestInheritanceChainCycle + extends TestInheritanceChainCycle + version 52:0 +{ + public Method "":"()V" + stack 1 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + public Method printMessage:"()V" + stack 2 locals 1 + { + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + ldc String "Hello, bug!"; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; + return; + } + +} // end Class TestInheritanceChainCycle