diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java new file mode 100644 index 000000000000..9ed3578d3d55 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -0,0 +1,463 @@ +/* + * 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; + } +} |