From 663631f0456fcc245dd835889f86541d75161c53 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 28 Aug 2014 20:52:43 +0400 Subject: java-decompiler: post-import cleanup (classes moved) --- .../modules/renamer/IdentifierConverter.java | 447 +++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java (limited to 'src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java') diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java new file mode 100644 index 0000000..634dc59 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -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 rootClasses = new ArrayList(); + + private List rootInterfaces = new ArrayList(); + + private HashMap> interfaceNameMaps = new HashMap>(); + + 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 lstClasses = getReversePostOrderListIterative(rootClasses); + + HashMap> classNameMaps = new HashMap>(); + + for(ClassWrapperNode node : lstClasses) { + + StructClass cl = node.getClassStruct(); + HashMap names = new HashMap(); + + // merge informations on super class + if(cl.superClass != null) { + HashMap mapClass = classNameMaps.get(cl.superClass.getString()); + if(mapClass != null) { + names.putAll(mapClass); + } + } + + // merge informations on interfaces + for(String intrName : cl.getInterfaceNames()) { + HashMap 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 processExternalInterface(StructClass cl) { + + HashMap names = new HashMap(); + + for(String intrName : cl.getInterfaceNames()) { + + HashMap 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 lstInterfaces = getReversePostOrderListIterative(rootInterfaces); + + HashMap> interfaceNameMaps = new HashMap>(); + + // rename methods and fields + for(ClassWrapperNode node : lstInterfaces) { + + StructClass cl = node.getClassStruct(); + HashMap names = new HashMap(); + + // merge informations on super interfaces + for(String intrName : cl.getInterfaceNames()) { + HashMap 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 lstAllClasses = new ArrayList(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 names) { + + // all classes are already renamed + String classOldFullName = cl.qualifiedName; + String classNewFullName = interceptor.getName(classOldFullName); + + if(classNewFullName == null) { + classNewFullName = classOldFullName; + } + + // methods + HashSet setMethodNames = new HashSet(); + for(StructMethod md : cl.getMethods()) { + setMethodNames.add(md.getName()); + } + + VBStyleCollection methods = cl.getMethods(); + for(int i=0;i setFieldNames = new HashSet(); + 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 getReversePostOrderListIterative(List roots) { + + List res = new ArrayList(); + + LinkedList stackNode = new LinkedList(); + LinkedList stackIndex = new LinkedList(); + + HashSet setVisited = new HashSet(); + + 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 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 nodes = new HashMap(); + HashMap classes = context.getClasses(); + + List rootClasses = new ArrayList(); + List rootInterfaces = new ArrayList(); + + for(StructClass cl : classes.values()) { + + if(!cl.isOwn()) { + continue; + } + + LinkedList stack = new LinkedList(); + LinkedList stackSubnodes = new LinkedList(); + + 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; + } + +} -- cgit v1.2.3