IDEA-285172 - [decompiler] - StrongConnectivityHelper refactoring
[idea/community.git] / plugins / java-decompiler / engine / src / org / jetbrains / java / decompiler / modules / renamer / IdentifierConverter.java
1 // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.java.decompiler.modules.renamer;
3
4 import org.jetbrains.java.decompiler.code.CodeConstants;
5 import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
6 import org.jetbrains.java.decompiler.struct.StructClass;
7 import org.jetbrains.java.decompiler.struct.StructContext;
8 import org.jetbrains.java.decompiler.struct.StructField;
9 import org.jetbrains.java.decompiler.struct.StructMethod;
10 import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
11 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
12 import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder;
13 import org.jetbrains.java.decompiler.util.VBStyleCollection;
14
15 import java.io.IOException;
16 import java.util.*;
17
18 public class IdentifierConverter implements NewClassNameBuilder {
19   private final StructContext context;
20   private final IIdentifierRenamer helper;
21   private final PoolInterceptor interceptor;
22   private List<ClassWrapperNode> rootClasses = new ArrayList<>();
23   private List<ClassWrapperNode> rootInterfaces = new ArrayList<>();
24   private Map<String, Map<String, String>> interfaceNameMaps = new HashMap<>();
25
26   public IdentifierConverter(StructContext context, IIdentifierRenamer helper, PoolInterceptor interceptor) {
27     this.context = context;
28     this.helper = helper;
29     this.interceptor = interceptor;
30   }
31
32   public void rename() {
33     try {
34       buildInheritanceTree();
35       renameAllClasses();
36       renameInterfaces();
37       renameClasses();
38       context.reloadContext();
39     }
40     catch (IOException ex) {
41       throw new RuntimeException("Renaming failed!");
42     }
43   }
44
45   private void renameClasses() {
46     List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses);
47     Map<String, Map<String, String>> classNameMaps = new HashMap<>();
48
49     for (ClassWrapperNode node : lstClasses) {
50       StructClass cl = node.getClassStruct();
51       Map<String, String> names = new HashMap<>();
52
53       // merge information on super class
54       if (cl.superClass != null) {
55         Map<String, String> mapClass = classNameMaps.get(cl.superClass.getString());
56         if (mapClass != null) {
57           names.putAll(mapClass);
58         }
59       }
60
61       // merge information on interfaces
62       for (String ifName : cl.getInterfaceNames()) {
63         Map<String, String> mapInt = interfaceNameMaps.get(ifName);
64         if (mapInt != null) {
65           names.putAll(mapInt);
66         }
67         else {
68           StructClass clintr = context.getClass(ifName);
69           if (clintr != null) {
70             names.putAll(processExternalInterface(clintr));
71           }
72         }
73       }
74
75       renameClassIdentifiers(cl, names);
76
77       if (!node.getSubclasses().isEmpty()) {
78         classNameMaps.put(cl.qualifiedName, names);
79       }
80     }
81   }
82
83   private Map<String, String> processExternalInterface(StructClass cl) {
84     Map<String, String> names = new HashMap<>();
85
86     for (String ifName : cl.getInterfaceNames()) {
87       Map<String, String> mapInt = interfaceNameMaps.get(ifName);
88       if (mapInt != null) {
89         names.putAll(mapInt);
90       }
91       else {
92         StructClass clintr = context.getClass(ifName);
93         if (clintr != null) {
94           names.putAll(processExternalInterface(clintr));
95         }
96       }
97     }
98
99     renameClassIdentifiers(cl, names);
100
101     return names;
102   }
103
104   private void renameInterfaces() {
105     List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);
106     Map<String, Map<String, String>> interfaceNameMaps = new HashMap<>();
107
108     // rename methods and fields
109     for (ClassWrapperNode node : lstInterfaces) {
110
111       StructClass cl = node.getClassStruct();
112       Map<String, String> names = new HashMap<>();
113
114       // merge information on super interfaces
115       for (String ifName : cl.getInterfaceNames()) {
116         Map<String, String> mapInt = interfaceNameMaps.get(ifName);
117         if (mapInt != null) {
118           names.putAll(mapInt);
119         }
120       }
121
122       renameClassIdentifiers(cl, names);
123
124       interfaceNameMaps.put(cl.qualifiedName, names);
125     }
126
127     this.interfaceNameMaps = interfaceNameMaps;
128   }
129
130   private void renameAllClasses() {
131     // order not important
132     List<ClassWrapperNode> lstAllClasses = new ArrayList<>(getReversePostOrderListIterative(rootInterfaces));
133     lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses));
134
135     // rename all interfaces and classes
136     for (ClassWrapperNode node : lstAllClasses) {
137       renameClass(node.getClassStruct());
138     }
139   }
140
141   private void renameClass(StructClass cl) {
142
143     if (!cl.isOwn()) {
144       return;
145     }
146
147     String classOldFullName = cl.qualifiedName;
148
149     // TODO: rename packages
150     String clSimpleName = ConverterHelper.getSimpleClassName(classOldFullName);
151     if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, clSimpleName, null, null)) {
152       String classNewFullName;
153
154       do {
155         String classname = helper.getNextClassName(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName));
156         classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, classname);
157       }
158       while (context.getClasses().containsKey(classNewFullName));
159
160       interceptor.addName(classOldFullName, classNewFullName);
161     }
162   }
163
164   private void renameClassIdentifiers(StructClass cl, Map<String, String> names) {
165     // all classes are already renamed
166     String classOldFullName = cl.qualifiedName;
167     String classNewFullName = interceptor.getName(classOldFullName);
168
169     if (classNewFullName == null) {
170       classNewFullName = classOldFullName;
171     }
172
173     // methods
174     HashSet<String> setMethodNames = new HashSet<>();
175     for (StructMethod md : cl.getMethods()) {
176       setMethodNames.add(md.getName());
177     }
178
179     VBStyleCollection<StructMethod, String> methods = cl.getMethods();
180     for (int i = 0; i < methods.size(); i++) {
181
182       StructMethod mt = methods.get(i);
183       String key = methods.getKey(i);
184
185       boolean isPrivate = mt.hasModifier(CodeConstants.ACC_PRIVATE);
186
187       String name = mt.getName();
188       if (!cl.isOwn() || mt.hasModifier(CodeConstants.ACC_NATIVE)) {
189         // external and native methods must not be renamed
190         if (!isPrivate) {
191           names.put(key, name);
192         }
193       }
194       else if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) {
195         if (isPrivate || !names.containsKey(key)) {
196           do {
197             name = helper.getNextMethodName(classOldFullName, name, mt.getDescriptor());
198           }
199           while (setMethodNames.contains(name));
200
201           if (!isPrivate) {
202             names.put(key, name);
203           }
204         }
205         else {
206           name = names.get(key);
207         }
208
209         interceptor.addName(classOldFullName + " " + mt.getName() + " " + mt.getDescriptor(),
210                             classNewFullName + " " + name + " " + buildNewDescriptor(false, mt.getDescriptor()));
211       }
212     }
213
214     // external fields are not being renamed
215     if (!cl.isOwn()) {
216       return;
217     }
218
219     // fields
220     // FIXME: should overloaded fields become the same name?
221     HashSet<String> setFieldNames = new HashSet<>();
222     for (StructField fd : cl.getFields()) {
223       setFieldNames.add(fd.getName());
224     }
225
226     for (StructField fd : cl.getFields()) {
227       if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) {
228         String newName;
229         do {
230           newName = helper.getNextFieldName(classOldFullName, fd.getName(), fd.getDescriptor());
231         }
232         while (setFieldNames.contains(newName));
233
234         interceptor.addName(classOldFullName + " " + fd.getName() + " " + fd.getDescriptor(),
235                             classNewFullName + " " + newName + " " + buildNewDescriptor(true, fd.getDescriptor()));
236       }
237     }
238   }
239
240   @Override
241   public String buildNewClassname(String className) {
242     return interceptor.getName(className);
243   }
244
245   private String buildNewDescriptor(boolean isField, String descriptor) {
246     String newDescriptor;
247     if (isField) {
248       newDescriptor = FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);
249     }
250     else {
251       newDescriptor = MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);
252     }
253     return newDescriptor != null ? newDescriptor : descriptor;
254   }
255
256   private static List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {
257     List<ClassWrapperNode> res = new ArrayList<>();
258
259     LinkedList<ClassWrapperNode> stackNode = new LinkedList<>();
260     LinkedList<Integer> stackIndex = new LinkedList<>();
261
262     Set<ClassWrapperNode> setVisited = new HashSet<>();
263
264     for (ClassWrapperNode root : roots) {
265       stackNode.add(root);
266       stackIndex.add(0);
267     }
268
269     while (!stackNode.isEmpty()) {
270       ClassWrapperNode node = stackNode.getLast();
271       int index = stackIndex.removeLast();
272
273       setVisited.add(node);
274
275       List<ClassWrapperNode> lstSubs = node.getSubclasses();
276
277       for (; index < lstSubs.size(); index++) {
278         ClassWrapperNode sub = lstSubs.get(index);
279         if (!setVisited.contains(sub)) {
280           stackIndex.add(index + 1);
281           stackNode.add(sub);
282           stackIndex.add(0);
283           break;
284         }
285       }
286
287       if (index == lstSubs.size()) {
288         res.add(0, node);
289         stackNode.removeLast();
290       }
291     }
292
293     return res;
294   }
295
296   private void buildInheritanceTree() {
297     Map<String, ClassWrapperNode> nodes = new HashMap<>();
298     Map<String, StructClass> classes = context.getClasses();
299
300     List<ClassWrapperNode> rootClasses = new ArrayList<>();
301     List<ClassWrapperNode> rootInterfaces = new ArrayList<>();
302
303     for (StructClass cl : classes.values()) {
304       if (!cl.isOwn()) {
305         continue;
306       }
307
308       LinkedList<StructClass> stack = new LinkedList<>();
309       LinkedList<ClassWrapperNode> stackSubNodes = new LinkedList<>();
310
311       stack.add(cl);
312       stackSubNodes.add(null);
313
314       while (!stack.isEmpty()) {
315         StructClass clStr = stack.removeFirst();
316         ClassWrapperNode child = stackSubNodes.removeFirst();
317
318         ClassWrapperNode node = nodes.get(clStr.qualifiedName);
319         boolean isNewNode = (node == null);
320
321         if (isNewNode) {
322           nodes.put(clStr.qualifiedName, node = new ClassWrapperNode(clStr));
323         }
324
325         if (child != null) {
326           node.addSubclass(child);
327         }
328
329         if (!isNewNode) {
330           break;
331         }
332         else {
333           boolean isInterface = clStr.hasModifier(CodeConstants.ACC_INTERFACE);
334           boolean found_parent = false;
335
336           if (isInterface) {
337             for (String ifName : clStr.getInterfaceNames()) {
338               StructClass clParent = classes.get(ifName);
339               if (clParent != null) {
340                 stack.add(clParent);
341                 stackSubNodes.add(node);
342                 found_parent = true;
343               }
344             }
345           }
346           else if (clStr.superClass != null) { // null iff java/lang/Object
347             StructClass clParent = classes.get(clStr.superClass.getString());
348             if (clParent != null) {
349               stack.add(clParent);
350               stackSubNodes.add(node);
351               found_parent = true;
352             }
353           }
354
355           if (!found_parent) { // no super class or interface
356             (isInterface ? rootInterfaces : rootClasses).add(node);
357           }
358         }
359       }
360     }
361
362     this.rootClasses = rootClasses;
363     this.rootInterfaces = rootInterfaces;
364   }
365 }