refactor: use MethodInfo for unresolved methods usage, improve related code
This commit is contained in:
@@ -8,10 +8,10 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
|
|
||||||
import jadx.api.metadata.ICodeAnnotation;
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
import jadx.api.metadata.ICodeNodeRef;
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
@@ -73,7 +73,7 @@ public final class JavaMethod implements JavaNode {
|
|||||||
return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUsed());
|
return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IMethodRef> getUnresolvedUsed() {
|
public List<MethodInfo> getUnresolvedUsed() {
|
||||||
return mth.getUnresolvedUsed();
|
return mth.getUnresolvedUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package jadx.api.usage;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -21,7 +21,7 @@ public interface IUsageInfoVisitor {
|
|||||||
|
|
||||||
void visitMethodsUses(MethodNode mth, List<MethodNode> methods);
|
void visitMethodsUses(MethodNode mth, List<MethodNode> methods);
|
||||||
|
|
||||||
void visitUnresolvedMethodsUsage(MethodNode mth, List<IMethodRef> methods);
|
void visitUnresolvedMethodsUsage(MethodNode mth, List<MethodInfo> methods);
|
||||||
|
|
||||||
void visitIsSelfCall(MethodNode mth, boolean isSelfCall);
|
void visitIsSelfCall(MethodNode mth, boolean isSelfCall);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package jadx.core.codegen;
|
package jadx.core.codegen;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -30,6 +32,10 @@ public class TypeGen {
|
|||||||
return stype.getShortName();
|
return stype.getShortName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> signatures(List<ArgType> types) {
|
||||||
|
return Utils.collectionMap(types, TypeGen::signature);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert literal arg to string (preferred method)
|
* Convert literal arg to string (preferred method)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -746,6 +746,14 @@ public class ClassNode extends NotificationAttrNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getInnerClassesRecursive(Set<ClassNode> resultClassesSet) {
|
||||||
|
for (ClassNode innerCls : innerClasses) {
|
||||||
|
if (resultClassesSet.add(innerCls)) {
|
||||||
|
innerCls.getInnerAndInlinedClassesRecursive(resultClassesSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addInnerClass(ClassNode cls) {
|
public void addInnerClass(ClassNode cls) {
|
||||||
if (innerClasses.isEmpty()) {
|
if (innerClasses.isEmpty()) {
|
||||||
innerClasses = new ArrayList<>(5);
|
innerClasses = new ArrayList<>(5);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import jadx.api.metadata.annotations.VarNode;
|
|||||||
import jadx.api.plugins.input.data.ICodeReader;
|
import jadx.api.plugins.input.data.ICodeReader;
|
||||||
import jadx.api.plugins.input.data.IDebugInfo;
|
import jadx.api.plugins.input.data.IDebugInfo;
|
||||||
import jadx.api.plugins.input.data.IMethodData;
|
import jadx.api.plugins.input.data.IMethodData;
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
|
||||||
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;
|
import jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;
|
||||||
import jadx.api.utils.CodeUtils;
|
import jadx.api.utils.CodeUtils;
|
||||||
@@ -87,7 +86,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
// Methods that use this method
|
// Methods that use this method
|
||||||
private List<MethodNode> useIn = Collections.emptyList();
|
private List<MethodNode> useIn = Collections.emptyList();
|
||||||
// Unresolved methods that use this method
|
// Unresolved methods that use this method
|
||||||
private List<IMethodRef> unresolvedUsed = Collections.emptyList();
|
private List<MethodInfo> unresolvedUsed = Collections.emptyList();
|
||||||
// Methods that this method uses
|
// Methods that this method uses
|
||||||
private Set<MethodNode> methodsUsed = new HashSet<>();
|
private Set<MethodNode> methodsUsed = new HashSet<>();
|
||||||
// True if this method contains a self call
|
// True if this method contains a self call
|
||||||
@@ -744,11 +743,11 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
return methodsUsed;
|
return methodsUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IMethodRef> getUnresolvedUsed() {
|
public List<MethodInfo> getUnresolvedUsed() {
|
||||||
return unresolvedUsed;
|
return unresolvedUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnresolvedUsed(List<IMethodRef> unresolvedUsed) {
|
public void setUnresolvedUsed(List<MethodInfo> unresolvedUsed) {
|
||||||
this.unresolvedUsed = unresolvedUsed;
|
this.unresolvedUsed = unresolvedUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
|
||||||
import jadx.api.usage.IUsageInfoData;
|
import jadx.api.usage.IUsageInfoData;
|
||||||
import jadx.api.usage.IUsageInfoVisitor;
|
import jadx.api.usage.IUsageInfoVisitor;
|
||||||
import jadx.core.clsp.ClspClass;
|
import jadx.core.clsp.ClspClass;
|
||||||
import jadx.core.clsp.ClspClassSource;
|
import jadx.core.clsp.ClspClassSource;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
@@ -37,8 +37,8 @@ public class UsageInfo implements IUsageInfoData {
|
|||||||
private final UseSet<MethodNode, MethodNode> mthUsage = new UseSet<>();
|
private final UseSet<MethodNode, MethodNode> mthUsage = new UseSet<>();
|
||||||
// MethodNodeA -> Set of MethodNodes that MethodNodeA calls
|
// MethodNodeA -> Set of MethodNodes that MethodNodeA calls
|
||||||
private final UseSet<MethodNode, MethodNode> mthUses = new UseSet<>();
|
private final UseSet<MethodNode, MethodNode> mthUses = new UseSet<>();
|
||||||
// MethodNodeA -> Set of IMethodRefs for methods that MethodNodeA calls that cannot be resolved
|
// MethodNodeA -> Set of MethodInfos for methods that MethodNodeA calls that cannot be resolved
|
||||||
private final UseSet<MethodNode, IMethodRef> unresolvedMthUsage = new UseSet<>();
|
private final UseSet<MethodNode, MethodInfo> unresolvedMthUsage = new UseSet<>();
|
||||||
private final Map<MethodNode, Boolean> selfCalls = new HashMap<>();
|
private final Map<MethodNode, Boolean> selfCalls = new HashMap<>();
|
||||||
|
|
||||||
public UsageInfo(RootNode root) {
|
public UsageInfo(RootNode root) {
|
||||||
@@ -53,7 +53,7 @@ public class UsageInfo implements IUsageInfoData {
|
|||||||
fieldUsage.visit((field, methods) -> field.setUseIn(resolveMthList(sortedList(methods))));
|
fieldUsage.visit((field, methods) -> field.setUseIn(resolveMthList(sortedList(methods))));
|
||||||
mthUsage.visit((mth, methods) -> mth.setUseIn(resolveMthList(sortedList(methods))));
|
mthUsage.visit((mth, methods) -> mth.setUseIn(resolveMthList(sortedList(methods))));
|
||||||
mthUses.visit((mth, methods) -> mth.setUsed(resolveMthList(sortedList(methods))));
|
mthUses.visit((mth, methods) -> mth.setUsed(resolveMthList(sortedList(methods))));
|
||||||
unresolvedMthUsage.visit((mth, unresolvedMethods) -> mth.setUnresolvedUsed(new ArrayList<>(unresolvedMethods)));
|
unresolvedMthUsage.visit((mth, unresolvedMethods) -> mth.setUnresolvedUsed(sortedList(unresolvedMethods)));
|
||||||
selfCalls.forEach(MethodNode::setCallsSelf);
|
selfCalls.forEach(MethodNode::setCallsSelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class UsageInfo implements IUsageInfoData {
|
|||||||
for (MethodNode mth : cls.getMethods()) {
|
for (MethodNode mth : cls.getMethods()) {
|
||||||
mth.setUseIn(resolveMthList(sortedList(mthUsage.getOrDefault(mth, Collections.emptySet()))));
|
mth.setUseIn(resolveMthList(sortedList(mthUsage.getOrDefault(mth, Collections.emptySet()))));
|
||||||
mth.setUsed(resolveMthList(sortedList(mthUses.getOrDefault(mth, Collections.emptySet()))));
|
mth.setUsed(resolveMthList(sortedList(mthUses.getOrDefault(mth, Collections.emptySet()))));
|
||||||
mth.setUnresolvedUsed(new ArrayList<>(unresolvedMthUsage.getOrDefault(mth, Collections.emptySet())));
|
mth.setUnresolvedUsed(sortedList(unresolvedMthUsage.getOrDefault(mth, Collections.emptySet())));
|
||||||
mth.setCallsSelf(selfCalls.getOrDefault(mth, false));
|
mth.setCallsSelf(selfCalls.getOrDefault(mth, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ public class UsageInfo implements IUsageInfoData {
|
|||||||
fieldUsage.visit((field, methods) -> visitor.visitFieldsUsage(field, resolveMthList(sortedList(methods))));
|
fieldUsage.visit((field, methods) -> visitor.visitFieldsUsage(field, resolveMthList(sortedList(methods))));
|
||||||
mthUsage.visit((mth, methods) -> visitor.visitMethodsUsage(mth, resolveMthList(sortedList(methods))));
|
mthUsage.visit((mth, methods) -> visitor.visitMethodsUsage(mth, resolveMthList(sortedList(methods))));
|
||||||
mthUses.visit((mth, methods) -> visitor.visitMethodsUses(mth, resolveMthList(sortedList(methods))));
|
mthUses.visit((mth, methods) -> visitor.visitMethodsUses(mth, resolveMthList(sortedList(methods))));
|
||||||
unresolvedMthUsage.visit((mth, unresolvedMethods) -> visitor.visitUnresolvedMethodsUsage(mth, new ArrayList<>(unresolvedMethods)));
|
unresolvedMthUsage.visit((mth, unresolvedMethods) -> visitor.visitUnresolvedMethodsUsage(mth, sortedList(unresolvedMethods)));
|
||||||
for (Entry<MethodNode, Boolean> entry : selfCalls.entrySet()) {
|
for (Entry<MethodNode, Boolean> entry : selfCalls.entrySet()) {
|
||||||
MethodNode mth = entry.getKey();
|
MethodNode mth = entry.getKey();
|
||||||
Boolean selfCall = entry.getValue();
|
Boolean selfCall = entry.getValue();
|
||||||
@@ -155,7 +155,7 @@ public class UsageInfo implements IUsageInfoData {
|
|||||||
/**
|
/**
|
||||||
* Add method usage: {@code useMth} occurrence found in {@code mth} code
|
* Add method usage: {@code useMth} occurrence found in {@code mth} code
|
||||||
*/
|
*/
|
||||||
public void unresolvedMethodUse(MethodNode mth, IMethodRef useMth) {
|
public void unresolvedMethodUse(MethodNode mth, MethodInfo useMth) {
|
||||||
unresolvedMthUsage.add(mth, useMth);
|
unresolvedMthUsage.add(mth, useMth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,14 +163,12 @@ public class UsageInfoVisitor extends AbstractVisitor {
|
|||||||
} else {
|
} else {
|
||||||
mthRef = insnData.getIndexAsMethod();
|
mthRef = insnData.getIndexAsMethod();
|
||||||
}
|
}
|
||||||
MethodNode methodNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
|
MethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);
|
||||||
|
MethodNode methodNode = root.resolveMethod(mthInfo);
|
||||||
if (methodNode != null) {
|
if (methodNode != null) {
|
||||||
usageInfo.methodUse(mth, methodNode);
|
usageInfo.methodUse(mth, methodNode);
|
||||||
} else {
|
} else {
|
||||||
mthRef.load();
|
usageInfo.unresolvedMethodUse(mth, mthInfo);
|
||||||
if (mthRef.getName() != null || mthRef.getParentClassType() != null) {
|
|
||||||
usageInfo.unresolvedMethodUse(mth, mthRef);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -181,11 +179,14 @@ public class UsageInfoVisitor extends AbstractVisitor {
|
|||||||
IMethodHandle methodHandle = InsnDataUtils.getMethodHandleAt(callSite, 4);
|
IMethodHandle methodHandle = InsnDataUtils.getMethodHandleAt(callSite, 4);
|
||||||
if (methodHandle != null) {
|
if (methodHandle != null) {
|
||||||
IMethodRef mthRef = methodHandle.getMethodRef();
|
IMethodRef mthRef = methodHandle.getMethodRef();
|
||||||
MethodNode mthNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
|
if (mthRef != null) {
|
||||||
if (mthNode != null) {
|
MethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);
|
||||||
usageInfo.methodUse(mth, mthNode);
|
MethodNode mthNode = root.resolveMethod(mthInfo);
|
||||||
} else if (mthRef.getName() != null || mthRef.getParentClassType() != null) {
|
if (mthNode != null) {
|
||||||
usageInfo.unresolvedMethodUse(mth, mthRef);
|
usageInfo.methodUse(mth, mthNode);
|
||||||
|
} else {
|
||||||
|
usageInfo.unresolvedMethodUse(mth, mthInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -7,19 +8,18 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.JavaMethod;
|
import jadx.api.JavaMethod;
|
||||||
import jadx.api.impl.SimpleCodeWriter;
|
import jadx.api.impl.SimpleCodeWriter;
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
|
||||||
import jadx.core.codegen.MethodGen;
|
import jadx.core.codegen.MethodGen;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttributeNode;
|
import jadx.core.dex.attributes.IAttributeNode;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.IfNode;
|
import jadx.core.dex.instructions.IfNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
@@ -477,19 +477,13 @@ public class DotGraphUtils {
|
|||||||
return methodNode.getAlias();
|
return methodNode.getAlias();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String unresolvedMethodFormatName(IMethodRef methodRef, boolean longName) {
|
public static String unresolvedMethodFormatName(MethodInfo mthInfo, boolean longName) {
|
||||||
String name = methodRef.getName();
|
String name = mthInfo.getName();
|
||||||
if (longName) {
|
if (longName) {
|
||||||
String className = methodRef.getParentClassType();
|
String className = mthInfo.getDeclClass().getFullName();
|
||||||
className = Utils.cleanObjectName(className);
|
String returnName = mthInfo.getReturnType().toString();
|
||||||
|
String argStr = Utils.listToString(mthInfo.getArgumentsTypes(), ArgType::toString);
|
||||||
String returnName = methodRef.getReturnType();
|
return String.format("%s.%s(%s):%s", className, name, argStr, returnName);
|
||||||
returnName = Utils.smaliNameToJavaName(returnName);
|
|
||||||
|
|
||||||
List<String> argTypes = methodRef.getArgTypes();
|
|
||||||
argTypes = argTypes.stream().map(c -> Utils.smaliNameToJavaName(c)).collect(Collectors.toList());
|
|
||||||
|
|
||||||
return String.format("%s.%s(%s):%s", className, name, Utils.listToString(argTypes), returnName);
|
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -508,4 +502,12 @@ public class DotGraphUtils {
|
|||||||
}
|
}
|
||||||
return arg.toString();
|
return arg.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String formatColor(Color color) {
|
||||||
|
return String.format("\"#%02x%02x%02x\"", color.getRed(), color.getGreen(), color.getBlue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toDotNodeName(String fullName) {
|
||||||
|
return fullName.replace("<", "\\<").replace(">", "\\>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ public class ListUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> List<T> mutableListOf(T... objs) {
|
public static <T> List<T> mutableListOf(T... objs) {
|
||||||
List<T> list = new ArrayList<>();
|
return new ArrayList<>(Arrays.asList(objs));
|
||||||
Collections.addAll(list, objs);
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> boolean isSingleElement(@Nullable List<T> list, T obj) {
|
public static <T> boolean isSingleElement(@Nullable List<T> list, T obj) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -368,6 +371,24 @@ public class StringUtils {
|
|||||||
return WORD_SEPARATORS.indexOf(chr) != -1;
|
return WORD_SEPARATORS.indexOf(chr) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> splitByFixedString(String content, String splitStr) {
|
||||||
|
if (isEmpty(content)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<String> parts = new ArrayList<>();
|
||||||
|
int splitLen = splitStr.length();
|
||||||
|
int pos = 0;
|
||||||
|
while (true) {
|
||||||
|
int split = content.indexOf(splitStr, pos);
|
||||||
|
if (split == -1) {
|
||||||
|
parts.add(content.substring(pos));
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
parts.add(content.substring(pos, split));
|
||||||
|
pos = split + splitLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String removeSuffix(String str, String suffix) {
|
public static String removeSuffix(String str, String suffix) {
|
||||||
if (str.endsWith(suffix)) {
|
if (str.endsWith(suffix)) {
|
||||||
return str.substring(0, str.length() - suffix.length());
|
return str.substring(0, str.length() - suffix.length());
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package jadx.gui.cache.usage;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
|
import jadx.core.codegen.TypeGen;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
|
|
||||||
public class CachedMethodRef implements IMethodRef {
|
public class CachedMethodRef implements IMethodRef {
|
||||||
|
|
||||||
@@ -18,6 +20,13 @@ public class CachedMethodRef implements IMethodRef {
|
|||||||
this.argTypes = argTypes;
|
this.argTypes = argTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CachedMethodRef(MethodInfo mthInfo) {
|
||||||
|
this(TypeGen.signature(mthInfo.getDeclClass().getType()),
|
||||||
|
mthInfo.getName(),
|
||||||
|
TypeGen.signature(mthInfo.getReturnType()),
|
||||||
|
TypeGen.signatures(mthInfo.getArgumentsTypes()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getParentClassType() {
|
public String getParentClassType() {
|
||||||
return parentClassType;
|
return parentClassType;
|
||||||
@@ -56,12 +65,10 @@ public class CachedMethodRef implements IMethodRef {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getUniqId() {
|
public int getUniqId() {
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getUniqId'");
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'load'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
import jadx.api.usage.IUsageInfoVisitor;
|
import jadx.api.usage.IUsageInfoVisitor;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -47,8 +48,8 @@ final class CollectUsageData implements IUsageInfoVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitUnresolvedMethodsUsage(MethodNode mth, List<IMethodRef> methods) {
|
public void visitUnresolvedMethodsUsage(MethodNode mth, List<MethodInfo> methods) {
|
||||||
data.getMethodData(mth).setUnresolvedUsage(methods);
|
data.getMethodData(mth).setUnresolvedUsage(mthInfoRef(methods));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -68,4 +69,8 @@ final class CollectUsageData implements IUsageInfoVisitor {
|
|||||||
private List<MthRef> mthNodesRef(List<MethodNode> methods) {
|
private List<MthRef> mthNodesRef(List<MethodNode> methods) {
|
||||||
return Utils.collectionMap(methods, m -> data.getMethodData(m).getMthRef());
|
return Utils.collectionMap(methods, m -> data.getMethodData(m).getMthRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<IMethodRef> mthInfoRef(List<MethodInfo> methods) {
|
||||||
|
return Utils.collectionMap(methods, CachedMethodRef::new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import java.util.Map;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
import jadx.api.usage.IUsageInfoData;
|
import jadx.api.usage.IUsageInfoData;
|
||||||
import jadx.api.usage.IUsageInfoVisitor;
|
import jadx.api.usage.IUsageInfoVisitor;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -60,7 +62,7 @@ class UsageData implements IUsageInfoData {
|
|||||||
if (mthUsageData != null) {
|
if (mthUsageData != null) {
|
||||||
mth.setUseIn(resolveMthList(mthUsageData.getUsage()));
|
mth.setUseIn(resolveMthList(mthUsageData.getUsage()));
|
||||||
mth.setUsed(resolveMthList(mthUsageData.getUses()));
|
mth.setUsed(resolveMthList(mthUsageData.getUses()));
|
||||||
mth.setUnresolvedUsed(mthUsageData.getUnresolvedUsage());
|
mth.setUnresolvedUsed(resolveMthInfoList(mthUsageData.getUnresolvedUsage()));
|
||||||
mth.setCallsSelf(mthUsageData.callsSelf());
|
mth.setCallsSelf(mthUsageData.callsSelf());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,4 +87,8 @@ class UsageData implements IUsageInfoData {
|
|||||||
private List<MethodNode> resolveMthList(List<MthRef> mthRefList) {
|
private List<MethodNode> resolveMthList(List<MthRef> mthRefList) {
|
||||||
return Utils.collectionMap(mthRefList, m -> root.resolveDirectMethod(m.getCls(), m.getShortId()));
|
return Utils.collectionMap(mthRefList, m -> root.resolveDirectMethod(m.getCls(), m.getShortId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<MethodInfo> resolveMthInfoList(List<IMethodRef> mthRefList) {
|
||||||
|
return Utils.collectionMap(mthRefList, m -> MethodInfo.fromRef(root, m));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
import jadx.api.usage.IUsageInfoData;
|
import jadx.api.usage.IUsageInfoData;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
import jadx.core.utils.files.FileUtils;
|
import jadx.core.utils.files.FileUtils;
|
||||||
@@ -38,10 +39,10 @@ import static java.nio.file.StandardOpenOption.WRITE;
|
|||||||
public class UsageFileAdapter extends DataAdapterHelper {
|
public class UsageFileAdapter extends DataAdapterHelper {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(UsageFileAdapter.class);
|
private static final Logger LOG = LoggerFactory.getLogger(UsageFileAdapter.class);
|
||||||
|
|
||||||
private static final int USAGE_DATA_VERSION = 2;
|
private static final int USAGE_DATA_VERSION = 3;
|
||||||
private static final byte[] JADX_USAGE_HEADER = "jadx.usage".getBytes(StandardCharsets.US_ASCII);
|
private static final byte[] JADX_USAGE_HEADER = "jadx.usage".getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
public static synchronized @Nullable RawUsageData load(Path usageFile, List<File> inputs) {
|
public static synchronized @Nullable RawUsageData load(RootNode root, Path usageFile, List<File> inputs) {
|
||||||
if (!Files.isRegularFile(usageFile)) {
|
if (!Files.isRegularFile(usageFile)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -61,7 +62,7 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
FileUtils.deleteFileIfExists(usageFile);
|
FileUtils.deleteFileIfExists(usageFile);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
RawUsageData data = readData(in);
|
RawUsageData data = readData(root, in);
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Loaded usage data from disk cache, classes count: {}, time: {}ms, file: {}",
|
LOG.debug("Loaded usage data from disk cache, classes count: {}, time: {}ms, file: {}",
|
||||||
data.getClsMap().size(), System.currentTimeMillis() - start, usageFile);
|
data.getClsMap().size(), System.currentTimeMillis() - start, usageFile);
|
||||||
@@ -103,7 +104,7 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RawUsageData readData(DataInputStream in) throws IOException {
|
private static RawUsageData readData(RootNode root, DataInputStream in) throws IOException {
|
||||||
RawUsageData data = new RawUsageData();
|
RawUsageData data = new RawUsageData();
|
||||||
int clsCount = readUVInt(in);
|
int clsCount = readUVInt(in);
|
||||||
int clsWithoutDataCount = readUVInt(in);
|
int clsWithoutDataCount = readUVInt(in);
|
||||||
@@ -120,6 +121,11 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
for (int i = 0; i < clsWithoutDataCount; i++) {
|
for (int i = 0; i < clsWithoutDataCount; i++) {
|
||||||
clsNames[c++] = in.readUTF();
|
clsNames[c++] = in.readUTF();
|
||||||
}
|
}
|
||||||
|
int uClsCount = readUVInt(in);
|
||||||
|
String[] uClsNames = new String[uClsCount];
|
||||||
|
for (int i = 0; i < uClsCount; i++) {
|
||||||
|
uClsNames[i] = in.readUTF();
|
||||||
|
}
|
||||||
|
|
||||||
// Method information
|
// Method information
|
||||||
int mthCount = readUVInt(in);
|
int mthCount = readUVInt(in);
|
||||||
@@ -137,16 +143,15 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
int uMthCount = readUVInt(in);
|
int uMthCount = readUVInt(in);
|
||||||
IMethodRef[] unresolvedMethods = new IMethodRef[uMthCount];
|
IMethodRef[] unresolvedMethods = new IMethodRef[uMthCount];
|
||||||
for (int i = 0; i < uMthCount; i++) {
|
for (int i = 0; i < uMthCount; i++) {
|
||||||
|
int clsId = readUVInt(in);
|
||||||
String name = in.readUTF();
|
String name = in.readUTF();
|
||||||
String parentClassType = in.readUTF();
|
|
||||||
String returnType = in.readUTF();
|
String returnType = in.readUTF();
|
||||||
int argCount = in.readInt();
|
int argCount = readUVInt(in);
|
||||||
String[] args = new String[argCount];
|
List<String> args = new ArrayList<>(argCount);
|
||||||
for (int j = 0; j < argCount; j++) {
|
for (int j = 0; j < argCount; j++) {
|
||||||
args[j] = in.readUTF();
|
args.add(in.readUTF());
|
||||||
}
|
}
|
||||||
IMethodRef iMethodRef = new CachedMethodRef(parentClassType, name, returnType, Arrays.asList(args));
|
unresolvedMethods[i] = new CachedMethodRef(uClsNames[clsId], name, returnType, args);
|
||||||
unresolvedMethods[i] = iMethodRef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage data
|
// Usage data
|
||||||
@@ -181,10 +186,29 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
Map<MthRef, Integer> mthMap = new HashMap<>();
|
Map<MthRef, Integer> mthMap = new HashMap<>();
|
||||||
Map<IMethodRef, Integer> uMthMap = new HashMap<>();
|
Map<IMethodRef, Integer> uMthMap = new HashMap<>();
|
||||||
Map<String, ClsUsageData> clsDataMap = usageData.getClsMap();
|
Map<String, ClsUsageData> clsDataMap = usageData.getClsMap();
|
||||||
|
|
||||||
|
Map<String, Integer> uClsMap = new HashMap<>();
|
||||||
|
List<IMethodRef> unresolvedMethods = clsDataMap.values().stream()
|
||||||
|
.flatMap(classUsageData -> classUsageData.getMthUsage().values().stream())
|
||||||
|
.flatMap(methodUsageData -> {
|
||||||
|
List<IMethodRef> unresolvedUsageList = methodUsageData.getUnresolvedUsage();
|
||||||
|
return unresolvedUsageList == null ? null : unresolvedUsageList.stream();
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<String> classes = new ArrayList<>(clsDataMap.keySet());
|
List<String> classes = new ArrayList<>(clsDataMap.keySet());
|
||||||
Collections.sort(classes);
|
Collections.sort(classes);
|
||||||
List<String> classesWithoutData = usageData.getClassesWithoutData();
|
List<String> classesWithoutData = usageData.getClassesWithoutData();
|
||||||
|
|
||||||
|
// pool for classes from unresolved methods
|
||||||
|
Set<String> uClsNames = new HashSet<>();
|
||||||
|
for (IMethodRef uMthRef : unresolvedMethods) {
|
||||||
|
uClsNames.add(uMthRef.getParentClassType());
|
||||||
|
}
|
||||||
|
List<String> uClsList = new ArrayList<>(uClsNames);
|
||||||
|
Collections.sort(uClsList);
|
||||||
|
|
||||||
// Class information
|
// Class information
|
||||||
writeUVInt(out, classes.size());
|
writeUVInt(out, classes.size());
|
||||||
writeUVInt(out, classesWithoutData.size());
|
writeUVInt(out, classesWithoutData.size());
|
||||||
@@ -198,6 +222,13 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
clsMap.put(cls, i++);
|
clsMap.put(cls, i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeUVInt(out, uClsList.size());
|
||||||
|
int u = 0;
|
||||||
|
for (String cls : uClsList) {
|
||||||
|
out.writeUTF(cls);
|
||||||
|
uClsMap.put(cls, u++);
|
||||||
|
}
|
||||||
|
|
||||||
// Method information
|
// Method information
|
||||||
List<MthRef> methods = clsDataMap.values().stream()
|
List<MthRef> methods = clsDataMap.values().stream()
|
||||||
.flatMap(c -> c.getMthUsage().values().stream())
|
.flatMap(c -> c.getMthUsage().values().stream())
|
||||||
@@ -212,33 +243,18 @@ public class UsageFileAdapter extends DataAdapterHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unresolved method information
|
// Unresolved method information
|
||||||
Set<IMethodRef> unresolvedMethods = clsDataMap.values().stream()
|
|
||||||
.flatMap(classUsageData -> classUsageData.getMthUsage().values().stream())
|
|
||||||
.flatMap(methodUsageData -> {
|
|
||||||
List<IMethodRef> unresolvedUsageList = methodUsageData.getUnresolvedUsage();
|
|
||||||
return (unresolvedUsageList == null) ? null : unresolvedUsageList.stream();
|
|
||||||
})
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
writeUVInt(out, unresolvedMethods.size());
|
writeUVInt(out, unresolvedMethods.size());
|
||||||
int k = 0;
|
int k = 0;
|
||||||
for (IMethodRef uMth : unresolvedMethods) {
|
for (IMethodRef uMthRef : unresolvedMethods) {
|
||||||
String name = uMth.getName();
|
writeUVInt(out, uClsMap.get(uMthRef.getParentClassType()));
|
||||||
out.writeUTF((name == null) ? "" : name);
|
out.writeUTF(uMthRef.getName());
|
||||||
String parentClassType = uMth.getParentClassType();
|
out.writeUTF(uMthRef.getReturnType());
|
||||||
out.writeUTF((parentClassType == null) ? "" : parentClassType);
|
List<String> args = uMthRef.getArgTypes();
|
||||||
String returnType = uMth.getReturnType();
|
writeUVInt(out, args.size());
|
||||||
out.writeUTF((returnType == null) ? "" : returnType);
|
for (String arg : args) {
|
||||||
List<String> argTypes = uMth.getArgTypes();
|
out.writeUTF(arg);
|
||||||
if (argTypes == null) {
|
|
||||||
out.writeInt(0);
|
|
||||||
} else {
|
|
||||||
out.writeInt(argTypes.size());
|
|
||||||
for (String arg : argTypes) {
|
|
||||||
out.writeUTF(arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
uMthMap.put(uMth, k++);
|
uMthMap.put(uMthRef, k++);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage data
|
// Usage data
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class UsageInfoCache implements IUsageInfoCache {
|
|||||||
}
|
}
|
||||||
synchronized (LOAD_DATA_SYNC) {
|
synchronized (LOAD_DATA_SYNC) {
|
||||||
if (rawUsageData == null) {
|
if (rawUsageData == null) {
|
||||||
rawUsageData = UsageFileAdapter.load(usageFile, inputs);
|
rawUsageData = UsageFileAdapter.load(root, usageFile, inputs);
|
||||||
}
|
}
|
||||||
if (rawUsageData != null) {
|
if (rawUsageData != null) {
|
||||||
UsageData data = new UsageData(root, rawUsageData);
|
UsageData data = new UsageData(root, rawUsageData);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ abstract class AbstractCodeAreaLine {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This could be itself or:
|
* This could be itself or:
|
||||||
* - the enclosing method delcaration if line is in a method
|
* - the enclosing method declaration if line is in a method
|
||||||
* - the enclosing class declaration if line is a field declaration
|
* - the enclosing class declaration if line is a field declaration
|
||||||
*/
|
*/
|
||||||
public IDeclaration getEnclosingScopeDeclaration() throws BadLocationException, FallbackSyncException {
|
public IDeclaration getEnclosingScopeDeclaration() throws BadLocationException, FallbackSyncException {
|
||||||
|
|||||||
@@ -25,14 +25,16 @@ import javax.swing.UIManager;
|
|||||||
|
|
||||||
import jadx.api.JavaMethod;
|
import jadx.api.JavaMethod;
|
||||||
import jadx.api.JavaNode;
|
import jadx.api.JavaNode;
|
||||||
import jadx.api.plugins.input.data.IMethodRef;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.utils.DotGraphUtils;
|
import jadx.core.utils.DotGraphUtils;
|
||||||
import jadx.gui.treemodel.JMethod;
|
import jadx.gui.treemodel.JMethod;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
|
||||||
import jadx.gui.utils.layout.WrapLayout;
|
import jadx.gui.utils.layout.WrapLayout;
|
||||||
|
|
||||||
|
import static jadx.core.utils.DotGraphUtils.formatColor;
|
||||||
|
import static jadx.core.utils.DotGraphUtils.toDotNodeName;
|
||||||
|
|
||||||
public class CallGraphDialog extends GraphDialog {
|
public class CallGraphDialog extends GraphDialog {
|
||||||
private static final long serialVersionUID = -850803763322590708L;
|
private static final long serialVersionUID = -850803763322590708L;
|
||||||
|
|
||||||
@@ -43,7 +45,7 @@ public class CallGraphDialog extends GraphDialog {
|
|||||||
private int calleeDepthLimit = 3;
|
private int calleeDepthLimit = 3;
|
||||||
private int nextNodeID;
|
private int nextNodeID;
|
||||||
private Map<JavaMethod, Integer> methodToNodeID;
|
private Map<JavaMethod, Integer> methodToNodeID;
|
||||||
private Map<IMethodRef, Integer> unresolvedMethodToNodeID;
|
private Map<MethodInfo, Integer> unresolvedMethodToNodeID;
|
||||||
private Set<Edge> edges;
|
private Set<Edge> edges;
|
||||||
private boolean longNames = false;
|
private boolean longNames = false;
|
||||||
|
|
||||||
@@ -222,10 +224,8 @@ public class CallGraphDialog extends GraphDialog {
|
|||||||
if (depth >= calleeDepthLimit) {
|
if (depth >= calleeDepthLimit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<IMethodRef> used = javaMethod.getUnresolvedUsed();
|
|
||||||
|
|
||||||
// add "calls" relationships
|
// add "calls" relationships
|
||||||
for (IMethodRef callee : used) {
|
for (MethodInfo callee : javaMethod.getUnresolvedUsed()) {
|
||||||
String name = callee.getName();
|
String name = callee.getName();
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
continue;
|
continue;
|
||||||
@@ -252,21 +252,21 @@ public class CallGraphDialog extends GraphDialog {
|
|||||||
methodToNodeID.put(method, nodeID);
|
methodToNodeID.put(method, nodeID);
|
||||||
}
|
}
|
||||||
String name = DotGraphUtils.methodFormatName(method, longNames);
|
String name = DotGraphUtils.methodFormatName(method, longNames);
|
||||||
f.format("Node_%d [ label=\"{%s}\" %s]\n", nodeID, UiUtils.toDotNodeName(name), extra);
|
f.format("Node_%d [ label=\"{%s}\" %s]\n", nodeID, toDotNodeName(name), extra);
|
||||||
if (javaMethod.callsSelf()) {
|
if (javaMethod.callsSelf()) {
|
||||||
addEdge(f, nodeID, nodeID);
|
addEdge(f, nodeID, nodeID);
|
||||||
}
|
}
|
||||||
return nodeID;
|
return nodeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int addNode(Formatter f, IMethodRef method) {
|
private int addNode(Formatter f, MethodInfo method) {
|
||||||
return addNode(f, method, "");
|
return addNode(f, method, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a node representing an unresolved method to the graph in f. Returns the ID of the new node
|
* Add a node representing an unresolved method to the graph in f. Returns the ID of the new node
|
||||||
*/
|
*/
|
||||||
private int addNode(Formatter f, IMethodRef method, String extra) {
|
private int addNode(Formatter f, MethodInfo method, String extra) {
|
||||||
int nodeID;
|
int nodeID;
|
||||||
if (unresolvedMethodToNodeID.containsKey(method)) {
|
if (unresolvedMethodToNodeID.containsKey(method)) {
|
||||||
nodeID = unresolvedMethodToNodeID.get(method);
|
nodeID = unresolvedMethodToNodeID.get(method);
|
||||||
@@ -275,15 +275,10 @@ public class CallGraphDialog extends GraphDialog {
|
|||||||
nextNodeID++;
|
nextNodeID++;
|
||||||
unresolvedMethodToNodeID.put(method, nodeID);
|
unresolvedMethodToNodeID.put(method, nodeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = DotGraphUtils.unresolvedMethodFormatName(method, longNames);
|
String name = DotGraphUtils.unresolvedMethodFormatName(method, longNames);
|
||||||
|
|
||||||
Color themeOutOfFocus = UIManager.getColor("Component.disabledBorderColor");
|
Color themeOutOfFocus = UIManager.getColor("Component.disabledBorderColor");
|
||||||
String outOfFocus =
|
String outOfFocus = "color=" + formatColor(themeOutOfFocus);
|
||||||
String.format("color=\"#%02x%02x%02x\"", themeOutOfFocus.getRed(), themeOutOfFocus.getGreen(),
|
f.format("Node_%d [ label=\"{%s}\" style=dashed %s %s]\n", nodeID, toDotNodeName(name), outOfFocus, extra);
|
||||||
themeOutOfFocus.getBlue());
|
|
||||||
|
|
||||||
f.format("Node_%d [ label=\"{%s}\" style=dashed %s %s]\n", nodeID, UiUtils.toDotNodeName(name), outOfFocus, extra);
|
|
||||||
return nodeID;
|
return nodeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ import jadx.core.dex.nodes.ClassNode;
|
|||||||
import jadx.core.dex.nodes.IMethodDetails;
|
import jadx.core.dex.nodes.IMethodDetails;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.DotGraphUtils;
|
import jadx.core.utils.DotGraphUtils;
|
||||||
|
import jadx.core.utils.StringUtils;
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
|
||||||
import jadx.gui.utils.layout.WrapLayout;
|
import jadx.gui.utils.layout.WrapLayout;
|
||||||
|
|
||||||
|
import static jadx.core.utils.DotGraphUtils.classFormatName;
|
||||||
|
import static jadx.core.utils.DotGraphUtils.formatColor;
|
||||||
|
import static jadx.core.utils.DotGraphUtils.toDotNodeName;
|
||||||
|
|
||||||
public class ClassInheritanceGraphDialog extends GraphDialog {
|
public class ClassInheritanceGraphDialog extends GraphDialog {
|
||||||
private static final long serialVersionUID = 938883901412562913L;
|
private static final long serialVersionUID = 938883901412562913L;
|
||||||
|
|
||||||
@@ -43,8 +47,9 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
private int nextNodeID = 0;
|
private int nextNodeID = 0;
|
||||||
|
|
||||||
public ClassInheritanceGraphDialog(MainWindow mainWindow, ClassNode cls) {
|
public ClassInheritanceGraphDialog(MainWindow mainWindow, ClassNode cls) {
|
||||||
super(mainWindow,
|
super(mainWindow, String.format("%s: %s",
|
||||||
String.format("%s: %s", NLS.str("graph_viewer.inheritance_graph.title"), DotGraphUtils.classFormatName(cls, false)));
|
NLS.str("graph_viewer.inheritance_graph.title"),
|
||||||
|
classFormatName(cls, false)));
|
||||||
this.cls = cls;
|
this.cls = cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +101,6 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String generateGraph(ClassNode rootClass) {
|
private String generateGraph(ClassNode rootClass) {
|
||||||
|
|
||||||
objectToNodeID = new HashMap<>();
|
objectToNodeID = new HashMap<>();
|
||||||
|
|
||||||
Color themeBackground = UIManager.getColor("Panel.background");
|
Color themeBackground = UIManager.getColor("Panel.background");
|
||||||
@@ -104,17 +108,11 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
Color themeHighlight = UIManager.getColor("Component.focusedBorderColor");
|
Color themeHighlight = UIManager.getColor("Component.focusedBorderColor");
|
||||||
Color themeShade = UIManager.getColor("TextArea.background");
|
Color themeShade = UIManager.getColor("TextArea.background");
|
||||||
|
|
||||||
String bgColor =
|
String bgColor = "bgcolor=" + formatColor(themeBackground);
|
||||||
String.format("bgcolor=\"#%02x%02x%02x\"", themeBackground.getRed(), themeBackground.getGreen(), themeBackground.getBlue());
|
String lineColor = "color=" + formatColor(themeForeground);
|
||||||
String lineColor =
|
String fontColor = "fontcolor=" + formatColor(themeForeground);
|
||||||
String.format("color=\"#%02x%02x%02x\"", themeForeground.getRed(), themeForeground.getGreen(), themeForeground.getBlue());
|
String highlightColor = "color=" + formatColor(themeHighlight);
|
||||||
String fontColor =
|
String shadeColor = "fillcolor=" + formatColor(themeShade);
|
||||||
String.format("fontcolor=\"#%02x%02x%02x\"", themeForeground.getRed(), themeForeground.getGreen(),
|
|
||||||
themeForeground.getBlue());
|
|
||||||
String highlightColor =
|
|
||||||
String.format("color=\"#%02x%02x%02x\"", themeHighlight.getRed(), themeHighlight.getGreen(),
|
|
||||||
themeHighlight.getBlue());
|
|
||||||
String shadeColor = String.format("fillcolor=\"#%02x%02x%02x\"", themeShade.getRed(), themeShade.getGreen(), themeShade.getBlue());
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
try (Formatter f = new Formatter(sb)) {
|
try (Formatter f = new Formatter(sb)) {
|
||||||
@@ -144,10 +142,7 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
int classID = addNode(f, cls, extra);
|
int classID = addNode(f, cls, extra);
|
||||||
|
|
||||||
// add interface relationships
|
// add interface relationships
|
||||||
List<ArgType> ifaces = cls.getInterfaces();
|
for (ArgType iface : cls.getInterfaces()) {
|
||||||
for (int i = 0; i < ifaces.size(); i++) {
|
|
||||||
ArgType iface = ifaces.get(i);
|
|
||||||
|
|
||||||
int ifaceID;
|
int ifaceID;
|
||||||
ClassNode ifaceNode = cls.root().resolveClass(iface);
|
ClassNode ifaceNode = cls.root().resolveClass(iface);
|
||||||
if (ifaceNode != null) {
|
if (ifaceNode != null) {
|
||||||
@@ -160,10 +155,9 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
String edgeLabel = cls.getAccessFlags().isInterface() ? "extends" : "implements";
|
String edgeLabel = cls.getAccessFlags().isInterface() ? "extends" : "implements";
|
||||||
f.format("Node_%d -> Node_%d [label=\"%s\" style=\"dashed\" ]\n", classID, ifaceID, edgeLabel);
|
f.format("Node_%d -> Node_%d [label=\"%s\" style=\"dashed\" ]\n", classID, ifaceID, edgeLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add superclass relationship
|
// add superclass relationship
|
||||||
ArgType superClass = cls.getSuperClass();
|
ArgType superClass = cls.getSuperClass();
|
||||||
if (superClass != ArgType.OBJECT) {
|
if (superClass != ArgType.OBJECT && superClass != null) {
|
||||||
int superClsID;
|
int superClsID;
|
||||||
cls = cls.root().resolveClass(superClass);
|
cls = cls.root().resolveClass(superClass);
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
@@ -194,8 +188,8 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
if (cls.getAccessFlags().isInterface()) {
|
if (cls.getAccessFlags().isInterface()) {
|
||||||
extra += " style=\"dashed, filled\"";
|
extra += " style=\"dashed, filled\"";
|
||||||
}
|
}
|
||||||
String name = DotGraphUtils.classFormatName(cls, longNames);
|
String name = classFormatName(cls, longNames);
|
||||||
f.format("Node_%d [ label=\"{%s\\ ", nodeID, UiUtils.toDotNodeName(name));
|
f.format("Node_%d [ label=\"{%s\\ ", nodeID, toDotNodeName(name));
|
||||||
if (overrides) {
|
if (overrides) {
|
||||||
f.format("|");
|
f.format("|");
|
||||||
List<Pair<String, String>> table = new ArrayList<>();
|
List<Pair<String, String>> table = new ArrayList<>();
|
||||||
@@ -207,12 +201,10 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
Formatter details = new Formatter();
|
Formatter details = new Formatter();
|
||||||
details.format(" overrides ");
|
details.format(" overrides ");
|
||||||
for (IMethodDetails baseMthDetails : ovrdAttr.getOverrideList()) {
|
for (IMethodDetails baseMthDetails : ovrdAttr.getOverrideList()) {
|
||||||
String baseClassName = DotGraphUtils.classFormatName(baseMthDetails.getMethodInfo().getDeclClass(), longNames);
|
String baseClassName = classFormatName(baseMthDetails.getMethodInfo().getDeclClass(), longNames);
|
||||||
details.format("%s, ", baseClassName);
|
details.format("%s, ", baseClassName);
|
||||||
}
|
}
|
||||||
String detailsString = details.toString();
|
String detailsString = StringUtils.removeSuffix(details.toString(), ", ");
|
||||||
// Remove trailing ', '
|
|
||||||
detailsString = detailsString.substring(0, detailsString.length() - 2);
|
|
||||||
table.add(Pair.of(methodName, detailsString));
|
table.add(Pair.of(methodName, detailsString));
|
||||||
details.close();
|
details.close();
|
||||||
}
|
}
|
||||||
@@ -231,7 +223,7 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
return nodeID;
|
return nodeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a node for an unresolved argtype
|
// Add a node for an unresolved arg type
|
||||||
private int addNode(Formatter f, ArgType argType) {
|
private int addNode(Formatter f, ArgType argType) {
|
||||||
return addNode(f, argType, "");
|
return addNode(f, argType, "");
|
||||||
}
|
}
|
||||||
@@ -246,10 +238,9 @@ public class ClassInheritanceGraphDialog extends GraphDialog {
|
|||||||
objectToNodeID.put(argType, nodeID);
|
objectToNodeID.put(argType, nodeID);
|
||||||
}
|
}
|
||||||
Color themeOutOfFocus = UIManager.getColor("Component.disabledBorderColor");
|
Color themeOutOfFocus = UIManager.getColor("Component.disabledBorderColor");
|
||||||
String outOfFocus =
|
String outOfFocus = "color=" + formatColor(themeOutOfFocus);
|
||||||
String.format("color=\"#%02x%02x%02x\"", themeOutOfFocus.getRed(), themeOutOfFocus.getGreen(), themeOutOfFocus.getBlue());
|
|
||||||
String name = DotGraphUtils.interfaceFormatName(argType, cls, longNames);
|
String name = DotGraphUtils.interfaceFormatName(argType, cls, longNames);
|
||||||
f.format("Node_%d [ label=\"{%s}\" %s %s]\n", nodeID, UiUtils.toDotNodeName(name), outOfFocus, extra);
|
f.format("Node_%d [ label=\"{%s}\" %s %s]\n", nodeID, toDotNodeName(name), outOfFocus, extra);
|
||||||
return nodeID;
|
return nodeID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import jadx.core.utils.DotGraphUtils;
|
|||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.ui.MainWindow;
|
import jadx.gui.ui.MainWindow;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.UiUtils;
|
|
||||||
import jadx.gui.utils.layout.WrapLayout;
|
import jadx.gui.utils.layout.WrapLayout;
|
||||||
|
|
||||||
|
import static jadx.core.utils.DotGraphUtils.formatColor;
|
||||||
|
import static jadx.core.utils.DotGraphUtils.toDotNodeName;
|
||||||
|
|
||||||
public class ClassMethodGraphDialog extends GraphDialog {
|
public class ClassMethodGraphDialog extends GraphDialog {
|
||||||
private static final long serialVersionUID = -850803763322590708L;
|
private static final long serialVersionUID = -850803763322590708L;
|
||||||
|
|
||||||
@@ -116,10 +118,6 @@ public class ClassMethodGraphDialog extends GraphDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String formatColor(Color color) {
|
|
||||||
return String.format("\"#%02x%02x%02x\"", color.getRed(), color.getGreen(), color.getBlue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCallers(int depth, Formatter f, JavaMethod javaMethod) {
|
private void addCallers(int depth, Formatter f, JavaMethod javaMethod) {
|
||||||
if (depth >= CALLER_DEPTH_LIMIT) {
|
if (depth >= CALLER_DEPTH_LIMIT) {
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +150,7 @@ public class ClassMethodGraphDialog extends GraphDialog {
|
|||||||
methodToNodeID.put(method, nodeID);
|
methodToNodeID.put(method, nodeID);
|
||||||
}
|
}
|
||||||
String name = DotGraphUtils.methodFormatName(method, longNames);
|
String name = DotGraphUtils.methodFormatName(method, longNames);
|
||||||
f.format("Node_%d [ label=\"{%s}\"]\n", nodeID, UiUtils.toDotNodeName(name));
|
f.format("Node_%d [ label=\"{%s}\"]\n", nodeID, toDotNodeName(name));
|
||||||
if (method.callsSelf()) {
|
if (method.callsSelf()) {
|
||||||
addEdge(f, nodeID, nodeID);
|
addEdge(f, nodeID, nodeID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -565,11 +565,4 @@ public class UiUtils {
|
|||||||
public static boolean nearlyEqual(float a, float b) {
|
public static boolean nearlyEqual(float a, float b) {
|
||||||
return Math.abs(a - b) < 1E-6f;
|
return Math.abs(a - b) < 1E-6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats a string to be in a .DOT node
|
|
||||||
public static String toDotNodeName(String fullName) {
|
|
||||||
String newName = fullName.replace("<", "\\<");
|
|
||||||
newName = newName.replace(">", "\\>");
|
|
||||||
return newName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user