You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
12 KiB
398 lines
12 KiB
/*
|
|
* 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.NewClassNameBuilder;
|
|
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
|
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
public class IdentifierConverter implements NewClassNameBuilder {
|
|
|
|
private StructContext context;
|
|
private IIdentifierRenamer helper;
|
|
private PoolInterceptor interceptor;
|
|
private List<ClassWrapperNode> rootClasses = new ArrayList<>();
|
|
private List<ClassWrapperNode> rootInterfaces = new ArrayList<>();
|
|
private Map<String, Map<String, String>> 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 ignored) { }
|
|
}
|
|
|
|
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);
|
|
Map<String, Map<String, String>> classNameMaps = new HashMap<>();
|
|
|
|
for (ClassWrapperNode node : lstClasses) {
|
|
StructClass cl = node.getClassStruct();
|
|
Map<String, String> names = new HashMap<>();
|
|
|
|
// merge information on super class
|
|
if (cl.superClass != null) {
|
|
Map<String, String> mapClass = classNameMaps.get(cl.superClass.getString());
|
|
if (mapClass != null) {
|
|
names.putAll(mapClass);
|
|
}
|
|
}
|
|
|
|
// merge information on interfaces
|
|
for (String ifName : cl.getInterfaceNames()) {
|
|
Map<String, String> 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<String, String> processExternalInterface(StructClass cl) {
|
|
Map<String, String> names = new HashMap<>();
|
|
|
|
for (String ifName : cl.getInterfaceNames()) {
|
|
Map<String, String> 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<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);
|
|
Map<String, Map<String, String>> interfaceNameMaps = new HashMap<>();
|
|
|
|
// rename methods and fields
|
|
for (ClassWrapperNode node : lstInterfaces) {
|
|
|
|
StructClass cl = node.getClassStruct();
|
|
Map<String, String> names = new HashMap<>();
|
|
|
|
// merge information on super interfaces
|
|
for (String ifName : cl.getInterfaceNames()) {
|
|
Map<String, String> 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<ClassWrapperNode> 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<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<>();
|
|
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.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<String> 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<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {
|
|
List<ClassWrapperNode> res = new ArrayList<>();
|
|
|
|
LinkedList<ClassWrapperNode> stackNode = new LinkedList<>();
|
|
LinkedList<Integer> stackIndex = new LinkedList<>();
|
|
|
|
Set<ClassWrapperNode> 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<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<>();
|
|
Map<String, StructClass> classes = context.getClasses();
|
|
|
|
List<ClassWrapperNode> rootClasses = new ArrayList<>();
|
|
List<ClassWrapperNode> rootInterfaces = new ArrayList<>();
|
|
|
|
for (StructClass cl : classes.values()) {
|
|
if (!cl.isOwn()) {
|
|
continue;
|
|
}
|
|
|
|
LinkedList<StructClass> stack = new LinkedList<>();
|
|
LinkedList<ClassWrapperNode> 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));
|
|
}
|
|
|
|
//noinspection ConstantConditions
|
|
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;
|
|
}
|
|
}
|
|
|