Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Decompiler] Fix short name imports shadowed by super inner classes (IDEA-196315) #854

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -5,6 +5,7 @@

import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext;
Expand All @@ -20,6 +21,7 @@ public class ImportCollector {
private final Set<String> setNotImportedNames = new HashSet<>();
// set of field names in this class and all its predecessors.
private final Set<String> setFieldNames = new HashSet<>();
private final Set<String> setInnerClassNames = new HashSet<>();
private final String currentPackageSlash;
private final String currentPackagePoint;

Expand All @@ -37,15 +39,37 @@ public ImportCollector(ClassNode root) {
}

Map<String, StructClass> classes = DecompilerContext.getStructContext().getClasses();
LinkedList<String> queue = new LinkedList<>();
StructClass currentClass = root.classStruct;
while (currentClass != null) {
if (currentClass.superClass != null) {
queue.add(currentClass.superClass.getString());
}

Collections.addAll(queue, currentClass.getInterfaceNames());

// all field names for the current class ..
for (StructField f : currentClass.getFields()) {
setFieldNames.add(f.getName());
}

// .. all inner classes for the current class ..
if (currentClass.hasAttribute(StructInnerClassesAttribute.ATTRIBUTE_INNER_CLASSES)) {
StructInnerClassesAttribute attribute =
(StructInnerClassesAttribute)currentClass.getAttribute(StructInnerClassesAttribute.ATTRIBUTE_INNER_CLASSES);

for (StructInnerClassesAttribute.Entry entry : attribute.getEntries()) {
if (entry.enclosingName != null && entry.enclosingName.equals(currentClass.qualifiedName)) {
setInnerClassNames.add(entry.simpleName);
}
}
}

// .. and traverse through parent.
currentClass = currentClass.superClass != null ? classes.get(currentClass.superClass.getString()) : null;
currentClass = !queue.isEmpty() ? classes.get(queue.removeFirst()) : null;
while (currentClass == null && !queue.isEmpty()) {
currentClass = classes.get(queue.removeFirst());
}
}
}

Expand Down Expand Up @@ -105,12 +129,14 @@ public String getShortName(String fullName, boolean imported) {

StructContext context = DecompilerContext.getStructContext();

// check for another class which could 'shadow' this one. Two cases:
// check for another class which could 'shadow' this one. Three cases:
// 1) class with the same short name in the current package
// 2) class with the same short name in the default package
// 3) inner class with the same short name in the current class, a super class, or an implemented interface
boolean existsDefaultClass =
(context.getClass(currentPackageSlash + shortName) != null && !packageName.equals(currentPackagePoint)) || // current package
(context.getClass(shortName) != null && !currentPackagePoint.isEmpty()); // default package
(context.getClass(shortName) != null && !currentPackagePoint.isEmpty()) || // default package
setInnerClassNames.contains(shortName); // inner class

if (existsDefaultClass ||
(mapSimpleNames.containsKey(shortName) && !packageName.equals(mapSimpleNames.get(shortName)))) {
Expand Down
Expand Up @@ -70,7 +70,8 @@ public void tearDown() {
@Test public void testAnonymousSignature() { doTest("pkg/TestAnonymousSignature"); }
@Test public void testLocalsSignature() { doTest("pkg/TestLocalsSignature"); }
@Test public void testParameterizedTypes() { doTest("pkg/TestParameterizedTypes"); }
@Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow"); }
@Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow",
"pkg/TestShadowingSuperClass"); }
@Test public void testStringConcat() { doTest("pkg/TestStringConcat"); }
@Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); }
@Test public void testMethodReferenceSameName() { doTest("pkg/TestMethodReferenceSameName"); }
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,7 @@
package pkg;

class TestShadowing {
class TestShadowing extends TestShadowingSuperClass {
ext.Shadow.B instanceOfB = new ext.Shadow.B();
java.util.Calendar.Builder calBuilder = new java.util.Calendar.Builder();
}

@@ -1,5 +1,12 @@
package pkg;

class TestShadowing {
import java.util.Calendar;

class TestShadowing extends TestShadowingSuperClass {
ext.Shadow.B instanceOfB = new ext.Shadow.B();
Calendar.Builder calBuilder = new Calendar.Builder();
}

class TestShadowingSuperClass {
static class Builder { }
}