| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.java.decompiler.modules.renamer; |
| |
| 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; |
| |
| import java.io.IOException; |
| import java.util.*; |
| |
| 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; |
| |
| // TODO: rename packages |
| String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName); |
| if (helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) { |
| String classNewFullName; |
| |
| 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); |
| |
| boolean isPrivate = mt.hasModifier(CodeConstants.ACC_PRIVATE); |
| |
| String name = mt.getName(); |
| if (!cl.isOwn() || mt.hasModifier(CodeConstants.ACC_NATIVE)) { |
| // 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 static 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() { |
| |
| Map<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); |
| Map<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.hasModifier(CodeConstants.ACC_INTERFACE); |
| 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; |
| } |
| } |