java-decompiler: post-import cleanup (classes moved)
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
package org.jetbrains.java.decompiler.modules.renamer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.StructContext;
|
||||
import org.jetbrains.java.decompiler.struct.StructField;
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
public class IdentifierConverter {
|
||||
|
||||
private StructContext context;
|
||||
|
||||
private IIdentifierRenamer helper;
|
||||
|
||||
private PoolInterceptor interceptor;
|
||||
|
||||
private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
|
||||
|
||||
private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
|
||||
|
||||
private HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>();
|
||||
|
||||
public void rename(StructContext context) {
|
||||
|
||||
try {
|
||||
this.context = context;
|
||||
|
||||
String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS);
|
||||
if(user_class != null) {
|
||||
try {
|
||||
helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance();
|
||||
} catch(Exception ex) {
|
||||
; // ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
if(helper == null) {
|
||||
helper = new ConverterHelper();
|
||||
}
|
||||
|
||||
interceptor = new PoolInterceptor(helper);
|
||||
|
||||
buildInheritanceTree();
|
||||
|
||||
renameAllClasses();
|
||||
|
||||
renameInterfaces();
|
||||
|
||||
renameClasses();
|
||||
|
||||
DecompilerContext.setPoolInterceptor(interceptor);
|
||||
context.reloadContext();
|
||||
|
||||
} catch(IOException ex){
|
||||
throw new RuntimeException("Renaming failed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void renameClasses() {
|
||||
|
||||
List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses);
|
||||
|
||||
HashMap<String, HashMap<String, String>> classNameMaps = new HashMap<String, HashMap<String, String>>();
|
||||
|
||||
for(ClassWrapperNode node : lstClasses) {
|
||||
|
||||
StructClass cl = node.getClassStruct();
|
||||
HashMap<String, String> names = new HashMap<String, String>();
|
||||
|
||||
// merge informations on super class
|
||||
if(cl.superClass != null) {
|
||||
HashMap<String, String> mapClass = classNameMaps.get(cl.superClass.getString());
|
||||
if(mapClass != null) {
|
||||
names.putAll(mapClass);
|
||||
}
|
||||
}
|
||||
|
||||
// merge informations on interfaces
|
||||
for(String intrName : cl.getInterfaceNames()) {
|
||||
HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
|
||||
if(mapInt != null) {
|
||||
names.putAll(mapInt);
|
||||
} else {
|
||||
StructClass clintr = context.getClass(intrName);
|
||||
if(clintr!=null) {
|
||||
names.putAll(processExternalInterface(clintr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renameClassIdentifiers(cl, names);
|
||||
|
||||
if(!node.getSubclasses().isEmpty()) {
|
||||
classNameMaps.put(cl.qualifiedName, names);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private HashMap<String, String> processExternalInterface(StructClass cl) {
|
||||
|
||||
HashMap<String, String> names = new HashMap<String, String>();
|
||||
|
||||
for(String intrName : cl.getInterfaceNames()) {
|
||||
|
||||
HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
|
||||
if(mapInt != null) {
|
||||
names.putAll(mapInt);
|
||||
} else {
|
||||
StructClass clintr = context.getClass(intrName);
|
||||
if(clintr!=null) {
|
||||
names.putAll(processExternalInterface(clintr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renameClassIdentifiers(cl, names);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private void renameInterfaces() {
|
||||
|
||||
List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);
|
||||
|
||||
HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>();
|
||||
|
||||
// rename methods and fields
|
||||
for(ClassWrapperNode node : lstInterfaces) {
|
||||
|
||||
StructClass cl = node.getClassStruct();
|
||||
HashMap<String, String> names = new HashMap<String, String>();
|
||||
|
||||
// merge informations on super interfaces
|
||||
for(String intrName : cl.getInterfaceNames()) {
|
||||
HashMap<String, String> mapInt = interfaceNameMaps.get(intrName);
|
||||
if(mapInt != null) {
|
||||
names.putAll(mapInt);
|
||||
}
|
||||
}
|
||||
|
||||
renameClassIdentifiers(cl, names);
|
||||
|
||||
interfaceNameMaps.put(cl.qualifiedName, names);
|
||||
}
|
||||
|
||||
this.interfaceNameMaps = interfaceNameMaps;
|
||||
}
|
||||
|
||||
private void renameAllClasses() {
|
||||
|
||||
// order not important
|
||||
List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces));
|
||||
lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses));
|
||||
|
||||
// rename all interfaces and classes
|
||||
for(ClassWrapperNode node : lstAllClasses) {
|
||||
renameClass(node.getClassStruct());
|
||||
}
|
||||
}
|
||||
|
||||
private void renameClass(StructClass cl) {
|
||||
|
||||
if(!cl.isOwn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String classOldFullName = cl.qualifiedName;
|
||||
String classNewFullName = classOldFullName;
|
||||
|
||||
// TODO: rename packages
|
||||
String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName);
|
||||
if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) {
|
||||
do {
|
||||
classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName,
|
||||
helper.getNextClassname(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName)));
|
||||
} while(context.getClasses().containsKey(classNewFullName));
|
||||
|
||||
interceptor.addName(classOldFullName, classNewFullName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void renameClassIdentifiers(StructClass cl, HashMap<String, String> names) {
|
||||
|
||||
// all classes are already renamed
|
||||
String classOldFullName = cl.qualifiedName;
|
||||
String classNewFullName = interceptor.getName(classOldFullName);
|
||||
|
||||
if(classNewFullName == null) {
|
||||
classNewFullName = classOldFullName;
|
||||
}
|
||||
|
||||
// methods
|
||||
HashSet<String> setMethodNames = new HashSet<String>();
|
||||
for(StructMethod md : cl.getMethods()) {
|
||||
setMethodNames.add(md.getName());
|
||||
}
|
||||
|
||||
VBStyleCollection<StructMethod, String> methods = cl.getMethods();
|
||||
for(int i=0;i<methods.size();i++) {
|
||||
|
||||
StructMethod mt = methods.get(i);
|
||||
String key = methods.getKey(i);
|
||||
|
||||
int access_flags = mt.getAccessFlags();
|
||||
boolean isPrivate = ((access_flags & CodeConstants.ACC_PRIVATE) != 0);
|
||||
|
||||
String name = mt.getName();
|
||||
if(!cl.isOwn() || (access_flags & CodeConstants.ACC_NATIVE) != 0) {
|
||||
// external and native methods must not be renamed
|
||||
if(!isPrivate) {
|
||||
names.put(key, name);
|
||||
}
|
||||
} else if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) {
|
||||
if(isPrivate || !names.containsKey(key)) {
|
||||
do {
|
||||
name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor());
|
||||
} while(setMethodNames.contains(name));
|
||||
|
||||
if(!isPrivate) {
|
||||
names.put(key, name);
|
||||
}
|
||||
} else {
|
||||
name = names.get(key);
|
||||
}
|
||||
|
||||
interceptor.addName(classOldFullName+" "+mt.getName()+" "+mt.getDescriptor(),
|
||||
classNewFullName+" "+name+" "+buildNewDescriptor(false, mt.getDescriptor()));
|
||||
}
|
||||
}
|
||||
|
||||
// external fields are not being renamed
|
||||
if(!cl.isOwn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fields
|
||||
// FIXME: should overloaded fields become the same name?
|
||||
HashSet<String> setFieldNames = new HashSet<String>();
|
||||
for(StructField fd : cl.getFields()) {
|
||||
setFieldNames.add(fd.getName());
|
||||
}
|
||||
|
||||
for(StructField fd : cl.getFields()) {
|
||||
if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) {
|
||||
String newname;
|
||||
|
||||
do {
|
||||
newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor());
|
||||
} while(setFieldNames.contains(newname));
|
||||
|
||||
interceptor.addName(classOldFullName+" "+fd.getName()+" "+fd.getDescriptor(),
|
||||
classNewFullName+" "+newname+" "+buildNewDescriptor(true, fd.getDescriptor()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String buildNewDescriptor(boolean isField, String descriptor) {
|
||||
|
||||
boolean updated = false;
|
||||
|
||||
if(isField) {
|
||||
FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor);
|
||||
|
||||
VarType ftype = fd.type;
|
||||
if(ftype.type == CodeConstants.TYPE_OBJECT) {
|
||||
String newclname = interceptor.getName(ftype.value);
|
||||
if(newclname != null) {
|
||||
ftype.value = newclname;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(updated) {
|
||||
return fd.getDescriptor();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor);
|
||||
// params
|
||||
for(VarType partype : md.params) {
|
||||
if(partype.type == CodeConstants.TYPE_OBJECT) {
|
||||
String newclname = interceptor.getName(partype.value);
|
||||
if(newclname != null) {
|
||||
partype.value = newclname;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return value
|
||||
if(md.ret.type == CodeConstants.TYPE_OBJECT) {
|
||||
String newclname = interceptor.getName(md.ret.value);
|
||||
if(newclname!=null) {
|
||||
md.ret.value = newclname;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(updated) {
|
||||
return md.getDescriptor();
|
||||
}
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {
|
||||
|
||||
List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>();
|
||||
|
||||
LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>();
|
||||
LinkedList<Integer> stackIndex = new LinkedList<Integer>();
|
||||
|
||||
HashSet<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>();
|
||||
|
||||
for(ClassWrapperNode root : roots) {
|
||||
stackNode.add(root);
|
||||
stackIndex.add(0);
|
||||
}
|
||||
|
||||
while(!stackNode.isEmpty()) {
|
||||
|
||||
ClassWrapperNode node = stackNode.getLast();
|
||||
int index = stackIndex.removeLast();
|
||||
|
||||
setVisited.add(node);
|
||||
|
||||
List<ClassWrapperNode> lstSubs = node.getSubclasses();
|
||||
|
||||
for(; index < lstSubs.size(); index++) {
|
||||
ClassWrapperNode sub = lstSubs.get(index);
|
||||
if(!setVisited.contains(sub)) {
|
||||
stackIndex.add(index+1);
|
||||
|
||||
stackNode.add(sub);
|
||||
stackIndex.add(0);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(index == lstSubs.size()) {
|
||||
res.add(0, node);
|
||||
|
||||
stackNode.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private void buildInheritanceTree() {
|
||||
|
||||
HashMap<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>();
|
||||
HashMap<String, StructClass> classes = context.getClasses();
|
||||
|
||||
List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
|
||||
List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
|
||||
|
||||
for(StructClass cl : classes.values()) {
|
||||
|
||||
if(!cl.isOwn()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LinkedList<StructClass> stack = new LinkedList<StructClass>();
|
||||
LinkedList<ClassWrapperNode> stackSubnodes = new LinkedList<ClassWrapperNode>();
|
||||
|
||||
stack.add(cl);
|
||||
stackSubnodes.add(null);
|
||||
|
||||
while(!stack.isEmpty()) {
|
||||
|
||||
StructClass clstr = stack.removeFirst();
|
||||
ClassWrapperNode child = stackSubnodes.removeFirst();
|
||||
|
||||
ClassWrapperNode node = nodes.get(clstr.qualifiedName);
|
||||
boolean isNewNode = (node == null);
|
||||
|
||||
if(isNewNode) {
|
||||
nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr));
|
||||
}
|
||||
|
||||
if(child!=null) {
|
||||
node.addSubclass(child);
|
||||
}
|
||||
|
||||
if(!isNewNode) {
|
||||
break;
|
||||
} else {
|
||||
|
||||
boolean isInterface = ((clstr.access_flags & CodeConstants.ACC_INTERFACE) != 0);
|
||||
boolean found_parent = false;
|
||||
|
||||
if(isInterface) {
|
||||
for(String intrName : clstr.getInterfaceNames()) {
|
||||
StructClass clparent = classes.get(intrName);
|
||||
if(clparent != null) {
|
||||
stack.add(clparent);
|
||||
stackSubnodes.add(node);
|
||||
found_parent = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(clstr.superClass != null) { // null iff java/lang/Object
|
||||
StructClass clparent = classes.get(clstr.superClass.getString());
|
||||
|
||||
if(clparent != null) {
|
||||
stack.add(clparent);
|
||||
stackSubnodes.add(node);
|
||||
found_parent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found_parent) { // no super class or interface
|
||||
(isInterface?rootInterfaces:rootClasses).add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.rootClasses = rootClasses;
|
||||
this.rootInterfaces = rootInterfaces;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user