// 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. package org.jetbrains.java.decompiler.modules.renamer; import org.jetbrains.java.decompiler.code.CodeConstants; 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.NewClassNameBuilder; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; import java.util.*; public class IdentifierConverter implements NewClassNameBuilder { private final StructContext context; private final IIdentifierRenamer helper; private final PoolInterceptor interceptor; private List rootClasses = new ArrayList<>(); private List rootInterfaces = new ArrayList<>(); private Map> interfaceNameMaps = new HashMap<>(); public IdentifierConverter(StructContext context, IIdentifierRenamer helper, PoolInterceptor interceptor) { this.context = context; this.helper = helper; this.interceptor = interceptor; } public void rename() { try { buildInheritanceTree(); renameAllClasses(); renameInterfaces(); renameClasses(); context.reloadContext(); } catch (IOException ex) { throw new RuntimeException("Renaming failed!"); } } private void renameClasses() { List lstClasses = getReversePostOrderListIterative(rootClasses); Map> classNameMaps = new HashMap<>(); for (ClassWrapperNode node : lstClasses) { StructClass cl = node.getClassStruct(); Map names = new HashMap<>(); // merge information on super class if (cl.superClass != null) { Map mapClass = classNameMaps.get(cl.superClass.getString()); if (mapClass != null) { names.putAll(mapClass); } } // merge information on interfaces for (String ifName : cl.getInterfaceNames()) { Map mapInt = interfaceNameMaps.get(ifName); if (mapInt != null) { names.putAll(mapInt); } else { StructClass clintr = context.getClass(ifName); if (clintr != null) { names.putAll(processExternalInterface(clintr)); } } } renameClassIdentifiers(cl, names); if (!node.getSubclasses().isEmpty()) { classNameMaps.put(cl.qualifiedName, names); } } } private Map processExternalInterface(StructClass cl) { Map names = new HashMap<>(); for (String ifName : cl.getInterfaceNames()) { Map mapInt = interfaceNameMaps.get(ifName); if (mapInt != null) { names.putAll(mapInt); } else { StructClass clintr = context.getClass(ifName); if (clintr != null) { names.putAll(processExternalInterface(clintr)); } } } renameClassIdentifiers(cl, names); return names; } private void renameInterfaces() { List lstInterfaces = getReversePostOrderListIterative(rootInterfaces); Map> interfaceNameMaps = new HashMap<>(); // rename methods and fields for (ClassWrapperNode node : lstInterfaces) { StructClass cl = node.getClassStruct(); Map names = new HashMap<>(); // merge information on super interfaces for (String ifName : cl.getInterfaceNames()) { Map mapInt = interfaceNameMaps.get(ifName); 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; // TODO: rename packages String clSimpleName = ConverterHelper.getSimpleClassName(classOldFullName); if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, clSimpleName, null, null)) { String classNewFullName; do { String classname = helper.getNextClassName(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName)); classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, classname); } while (context.getClasses().containsKey(classNewFullName)); interceptor.addName(classOldFullName, classNewFullName); } } private void renameClassIdentifiers(StructClass cl, Map 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 < 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.Type.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 setFieldNames = new HashSet<>(); for (StructField fd : cl.getFields()) { setFieldNames.add(fd.getName()); } for (StructField fd : cl.getFields()) { if (helper.toBeRenamed(IIdentifierRenamer.Type.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())); } } } @Override public String buildNewClassname(String className) { return interceptor.getName(className); } private String buildNewDescriptor(boolean isField, String descriptor) { String newDescriptor; if (isField) { newDescriptor = FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } else { newDescriptor = MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } return newDescriptor != null ? newDescriptor : descriptor; } private static List getReversePostOrderListIterative(List roots) { List res = new ArrayList<>(); LinkedList stackNode = new LinkedList<>(); LinkedList stackIndex = new LinkedList<>(); Set 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() { Map nodes = new HashMap<>(); Map 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.hasModifier(CodeConstants.ACC_INTERFACE); boolean found_parent = false; if (isInterface) { for (String ifName : clStr.getInterfaceNames()) { StructClass clParent = classes.get(ifName); 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; } }