Use fully qualified static field name in conflict #541
This commit is contained in:
committed by
Egor.Ushakov
parent
0b442fc64b
commit
2a213aa4a0
@@ -15,10 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.java.decompiler.main.collectors;
|
package org.jetbrains.java.decompiler.main.collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.java.decompiler.main.ClassesProcessor;
|
||||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||||
import org.jetbrains.java.decompiler.main.TextBuffer;
|
import org.jetbrains.java.decompiler.main.TextBuffer;
|
||||||
|
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||||
|
import org.jetbrains.java.decompiler.struct.StructField;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -28,6 +31,8 @@ public class ImportCollector {
|
|||||||
|
|
||||||
private final Map<String, String> mapSimpleNames = new HashMap<>();
|
private final Map<String, String> mapSimpleNames = new HashMap<>();
|
||||||
private final Set<String> setNotImportedNames = new HashSet<>();
|
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 String currentPackageSlash;
|
private final String currentPackageSlash;
|
||||||
private final String currentPackagePoint;
|
private final String currentPackagePoint;
|
||||||
|
|
||||||
@@ -43,6 +48,37 @@ public class ImportCollector {
|
|||||||
currentPackageSlash = "";
|
currentPackageSlash = "";
|
||||||
currentPackagePoint = "";
|
currentPackagePoint = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, ClassNode> mapRootCases = DecompilerContext.getClassProcessor().getMapRootClasses();
|
||||||
|
for(StructClass sClass = root.classStruct;
|
||||||
|
sClass!=null;
|
||||||
|
){
|
||||||
|
// all field names for current class ..
|
||||||
|
for(StructField f: sClass.getFields()) {
|
||||||
|
setFieldNames.add(f.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. and traverse through parent.
|
||||||
|
ClassNode classNode;
|
||||||
|
if(sClass.superClass==null || (classNode = (mapRootCases.get(sClass.superClass.getString())))==null)
|
||||||
|
break;
|
||||||
|
sClass = classNode.classStruct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the package-less name ClassName is shaded by variable in a context of
|
||||||
|
* the decompiled class
|
||||||
|
* @param classToName - pkg.name.ClassName - class to find shortname for
|
||||||
|
* @return ClassName if the name is not shaded by local field, pkg.name.ClassName otherwise
|
||||||
|
*/
|
||||||
|
public String getShortNameInClassContext(String classToName) {
|
||||||
|
String shortName = getShortName(classToName);
|
||||||
|
if(setFieldNames.contains(shortName)) {
|
||||||
|
return classToName;
|
||||||
|
} else {
|
||||||
|
return shortName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getShortName(String fullName) {
|
public String getShortName(String fullName) {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public class FieldExprent extends Exprent {
|
|||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
||||||
if (node == null || !classname.equals(node.classStruct.qualifiedName) || isAmbiguous()) {
|
if (node == null || !classname.equals(node.classStruct.qualifiedName) || isAmbiguous()) {
|
||||||
buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
|
buf.append(DecompilerContext.getImportCollector().getShortNameInClassContext(ExprProcessor.buildJavaClassName(classname)));
|
||||||
buf.append(".");
|
buf.append(".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2016 JetBrains s.r.o.
|
* Copyright 2000-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -225,7 +225,7 @@ public class InvocationExprent extends Exprent {
|
|||||||
|
|
||||||
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
|
||||||
if (node == null || !classname.equals(node.classStruct.qualifiedName)) {
|
if (node == null || !classname.equals(node.classStruct.qualifiedName)) {
|
||||||
buf.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(classname)));
|
buf.append(DecompilerContext.getImportCollector().getShortNameInClassContext(ExprProcessor.buildJavaClassName(classname)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -102,6 +102,11 @@ public class SingleClassesTest {
|
|||||||
@Test public void testAccessReplace() { doTest("pkg/TestAccessReplace"); }
|
@Test public void testAccessReplace() { doTest("pkg/TestAccessReplace"); }
|
||||||
@Test public void testStringLiterals() { doTest("pkg/TestStringLiterals"); }
|
@Test public void testStringLiterals() { doTest("pkg/TestStringLiterals"); }
|
||||||
@Test public void testPrimitives() { doTest("pkg/TestPrimitives"); }
|
@Test public void testPrimitives() { doTest("pkg/TestPrimitives"); }
|
||||||
|
@Test public void testClashName() { doTest("pkg/TestClashName", "pkg/SharedName1",
|
||||||
|
"pkg/SharedName2", "pkg/SharedName3", "pkg/SharedName4", "pkg/NonSharedName",
|
||||||
|
"pkg/TestClashNameParent", "ext/TestClashNameParent","pkg/TestClashNameIface", "ext/TestClashNameIface"); }
|
||||||
|
@Test public void testSwitchOnEnum() { doTest("pkg/TestSwitchOnEnum","pkg/TestSwitchOnEnum$1");}
|
||||||
|
|
||||||
|
|
||||||
private void doTest(String testFile, String... companionFiles) {
|
private void doTest(String testFile, String... companionFiles) {
|
||||||
ConsoleDecompiler decompiler = fixture.getDecompiler();
|
ConsoleDecompiler decompiler = fixture.getDecompiler();
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,7 +17,7 @@ class i implements bg {
|
|||||||
|
|
||||||
public void a(c var1, k var2, boolean var3) {
|
public void a(c var1, k var2, boolean var3) {
|
||||||
File var4 = this.a.b().a(var1);// 2
|
File var4 = this.a.b().a(var1);// 2
|
||||||
b.a(this.b).add(var4);// 3
|
a.a.a.a.e.f.b.a(this.b).add(var4);// 3
|
||||||
}// 4
|
}// 4
|
||||||
|
|
||||||
public void a(a.a.a.a.c.b var1) {
|
public void a(a.a.a.a.c.b var1) {
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package pkg;
|
||||||
|
|
||||||
|
@SharedName4
|
||||||
|
public class TestClashName extends ext.TestClashNameParent implements TestClashNameIface {
|
||||||
|
int TestClashNameParent = 0;
|
||||||
|
int TestClashNameIface = 0;
|
||||||
|
int SharedName1 = 0;
|
||||||
|
int SharedName4 = 0;
|
||||||
|
int SharedName5 = 0;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int k;
|
||||||
|
int l;
|
||||||
|
int m;
|
||||||
|
int n;
|
||||||
|
SharedName1 p;
|
||||||
|
SharedName5<SharedName1> q;
|
||||||
|
|
||||||
|
public TestClashName() {
|
||||||
|
this.i = pkg.SharedName1.f;// 59
|
||||||
|
this.j = NonSharedName.f;// 60
|
||||||
|
this.k = SharedName2.f;// 61
|
||||||
|
this.l = pkg.SharedName3.f;// 62
|
||||||
|
this.m = pkg.SharedName1.getF();// 63
|
||||||
|
this.n = NonSharedName.getF();// 64
|
||||||
|
this.p = null;// 65
|
||||||
|
this.q = null;// 67
|
||||||
|
}
|
||||||
|
|
||||||
|
@SharedName4
|
||||||
|
public int m() {
|
||||||
|
int var1 = this.i;// 73
|
||||||
|
pkg.SharedName1.f = this.j;// 74
|
||||||
|
int var2 = SharedName2.f;// 75
|
||||||
|
NonSharedName.f = this.k;// 76
|
||||||
|
int var3 = NonSharedName.f;// 77
|
||||||
|
return var1 + var2 + var3;// 78
|
||||||
|
}
|
||||||
|
|
||||||
|
public void f() {
|
||||||
|
}// 82
|
||||||
|
}
|
||||||
|
|
||||||
|
class 'pkg/TestClashName' {
|
||||||
|
method '<init> ()V' {
|
||||||
|
1e 19
|
||||||
|
21 19
|
||||||
|
25 20
|
||||||
|
28 20
|
||||||
|
2c 21
|
||||||
|
2f 21
|
||||||
|
33 22
|
||||||
|
36 22
|
||||||
|
3a 23
|
||||||
|
3d 23
|
||||||
|
41 24
|
||||||
|
44 24
|
||||||
|
48 25
|
||||||
|
49 25
|
||||||
|
4d 26
|
||||||
|
4e 26
|
||||||
|
51 27
|
||||||
|
}
|
||||||
|
|
||||||
|
method 'm ()I' {
|
||||||
|
1 31
|
||||||
|
4 31
|
||||||
|
6 32
|
||||||
|
9 32
|
||||||
|
c 33
|
||||||
|
f 33
|
||||||
|
11 34
|
||||||
|
14 34
|
||||||
|
17 35
|
||||||
|
1a 35
|
||||||
|
1d 36
|
||||||
|
1f 36
|
||||||
|
20 36
|
||||||
|
}
|
||||||
|
|
||||||
|
method 'f ()V' {
|
||||||
|
0 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lines mapping:
|
||||||
|
59 <-> 20
|
||||||
|
60 <-> 21
|
||||||
|
61 <-> 22
|
||||||
|
62 <-> 23
|
||||||
|
63 <-> 24
|
||||||
|
64 <-> 25
|
||||||
|
65 <-> 26
|
||||||
|
67 <-> 27
|
||||||
|
73 <-> 32
|
||||||
|
74 <-> 33
|
||||||
|
75 <-> 34
|
||||||
|
76 <-> 35
|
||||||
|
77 <-> 36
|
||||||
|
78 <-> 37
|
||||||
|
82 <-> 41
|
||||||
|
Not mapped:
|
||||||
|
52
|
||||||
|
53
|
||||||
|
54
|
||||||
|
55
|
||||||
|
56
|
||||||
|
57
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package pkg;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class TestSwitchOnEnum {
|
||||||
|
int myInt;
|
||||||
|
|
||||||
|
public void testSOE(TimeUnit var1) {
|
||||||
|
switch(null.$SwitchMap$java$util$concurrent$TimeUnit[var1.ordinal()]) {// 12
|
||||||
|
case 1:
|
||||||
|
return;// 14
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}// 16
|
||||||
|
}
|
||||||
|
|
||||||
|
class 'pkg/TestSwitchOnEnum' {
|
||||||
|
method 'testSOE (Ljava/util/concurrent/TimeUnit;)V' {
|
||||||
|
0 8
|
||||||
|
4 8
|
||||||
|
7 8
|
||||||
|
8 8
|
||||||
|
1c 10
|
||||||
|
1d 13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lines mapping:
|
||||||
|
12 <-> 9
|
||||||
|
14 <-> 11
|
||||||
|
16 <-> 14
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package ext;
|
||||||
|
interface TestClashNameIface {
|
||||||
|
public void foo();
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ext;
|
||||||
|
|
||||||
|
public class TestClashNameParent {
|
||||||
|
int SharedName3 = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package pkg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The names SharedName[123] are shared between variables in local|parent class and class names.
|
||||||
|
* Where approprate, the classes have to be referenced by fully qualified names
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SharedName1 {
|
||||||
|
static int f = 0;
|
||||||
|
|
||||||
|
static int getF() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedName2 {
|
||||||
|
static int f = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedName3 {
|
||||||
|
static int f = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NonSharedName {
|
||||||
|
static int f = 0;
|
||||||
|
|
||||||
|
static int getF() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface SharedName4 {
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedName5<T>{
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestClashNameParent {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestClashNameIface {
|
||||||
|
public void f();
|
||||||
|
}
|
||||||
|
// *** Legend for first sentence in comments below:
|
||||||
|
// (+) or (-) indicate whether 'pkg' prefix is or is not required when referencing *SharedName*.
|
||||||
|
// The sign is optionally followed by decompiler class name that generates the line that is being commented
|
||||||
|
// in a call of getShortName()
|
||||||
|
|
||||||
|
@SharedName4 // (-)AnnotationExprent. While a variable named SharedName4 does exist in the annotated class,
|
||||||
|
// lookup process for annotation names never includes variable names.
|
||||||
|
public class TestClashName extends ext.TestClashNameParent implements /*pkg.*/TestClashNameIface { // (+)(-)TextUtil
|
||||||
|
int TestClashNameParent = 0;
|
||||||
|
int TestClashNameIface = 0;
|
||||||
|
int SharedName1 = 0;
|
||||||
|
int SharedName4 = 0;
|
||||||
|
int SharedName5 = 0;
|
||||||
|
|
||||||
|
int i = pkg.SharedName1.f; // (+)FieldExprent. SharedName1 class name is shadowed by a variable in this class
|
||||||
|
int j = NonSharedName.f; // (-)FieldExprent. The NonSharedName is not used for other objects in the current scope
|
||||||
|
int k = SharedName2.f; // (-)FieldExprent. SharedName2 variable is not the current scope
|
||||||
|
int l = pkg.SharedName3.f; // (+)FieldExprent. SharedName3 class name is shadowed by a variable in parent class
|
||||||
|
int m = pkg.SharedName1.getF();// (+)InvocationExprent. SharedName1 class name is shadowed by a variable in this class
|
||||||
|
int n = NonSharedName.getF(); // (-)InvocationExprent. The NonSharedName is not used for other objects in the current scope
|
||||||
|
SharedName1 p = null; // (-)ExprProcessor. While a variable named SharedName1 in current scope does exist,
|
||||||
|
// namespace in type declaration does not include variable names in a scope
|
||||||
|
SharedName5<SharedName1> q = null;//(-)(-)GenericMain (both names). While a variable named SharedName1 does exist in current scope,
|
||||||
|
// lookup for generic parameters never includes variable names
|
||||||
|
|
||||||
|
@SharedName4 // (-)AnnotationExprent. While a variable named SharedName4 does exist in current scope,
|
||||||
|
// lookup process for annotation names never includes variable names.
|
||||||
|
public int m() {
|
||||||
|
int SharedName2 = i;
|
||||||
|
pkg.SharedName1.f = j; // (+)FieldExprent. SharedName1 class name is shadowed by a variable in this class
|
||||||
|
int x = pkg.SharedName2.f; // (+)FieldExprent. SharedName2 class name is shadowed by a variable in this method
|
||||||
|
NonSharedName.f = k; // (-)ExprProcessor. The NonSharedName is not used for other objects in the current scope
|
||||||
|
int y = NonSharedName.f; // (-)FieldExprent. The NonSharedName is not used for other objects in the current scope
|
||||||
|
return SharedName2 + x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void f() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package pkg;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This illustrates a bug in fernflower as of 20170421. Decompiled output of this class does not compile back.
|
||||||
|
*/
|
||||||
|
public class TestSwitchOnEnum {
|
||||||
|
|
||||||
|
int myInt;
|
||||||
|
|
||||||
|
public int testSOE(TimeUnit t) {
|
||||||
|
// This creates anonymous SwitchMap inner class.
|
||||||
|
switch (t) {
|
||||||
|
case SECONDS:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user