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 0000000..0969e39 Binary files /dev/null and b/testData/classes/pkg/TestInheritanceChainCycle.class differ 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