perf: improve processing of override related methods (#1072)

This commit is contained in:
Skylot
2020-12-31 13:33:18 +03:00
parent 1b8b377f90
commit b7ca898b77
8 changed files with 89 additions and 39 deletions
@@ -3,6 +3,8 @@ package jadx.api;
import java.util.Collections;
import java.util.List;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.MethodNode;
@@ -61,6 +63,15 @@ public final class JavaMethod implements JavaNode {
return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUseIn());
}
public List<JavaNode> getOverrideRelatedMethods() {
MethodOverrideAttr ovrdAttr = mth.get(AType.METHOD_OVERRIDE);
if (ovrdAttr == null) {
return Collections.emptyList();
}
JadxDecompiler decompiler = getDeclaringClass().getRootDecompiler();
return decompiler.convertNodes(ovrdAttr.getRelatedMthNodes());
}
public boolean isConstructor() {
return mth.getMethodInfo().isConstructor();
}
@@ -211,15 +211,22 @@ public class Deobfuscator {
private void renameMethod(MethodNode mth) {
String alias = getMethodAlias(mth);
if (alias != null) {
mth.getMethodInfo().setAlias(alias);
resolveOverriding(mth, alias);
applyMethodAlias(mth, alias);
}
}
public void forceRenameMethod(MethodNode mth) {
String alias = makeMethodAlias(mth);
mth.getMethodInfo().setAlias(alias);
resolveOverriding(mth, alias);
applyMethodAlias(mth, alias);
}
private void applyMethodAlias(MethodNode mth, String alias) {
MethodInfo methodInfo = mth.getMethodInfo();
methodInfo.setAlias(alias);
String prev = mthMap.put(methodInfo, alias);
if (prev == null) {
resolveOverriding(mth, alias);
}
}
private void resolveOverriding(MethodNode mth, String alias) {
@@ -512,12 +519,7 @@ public class Deobfuscator {
if (alias != null) {
return alias;
}
String presetAlias = deobfPresets.getForMth(methodInfo);
if (presetAlias != null) {
mthMap.put(methodInfo, presetAlias);
return presetAlias;
}
return null;
return deobfPresets.getForMth(methodInfo);
}
public String makeFieldAlias(FieldNode field) {
@@ -533,9 +535,7 @@ public class Deobfuscator {
} else {
prefix = "m";
}
String alias = String.format("%s%d%s", prefix, mthIndex++, prepareNamePart(mth.getName()));
mthMap.put(mth.getMethodInfo(), alias);
return alias;
return String.format("%s%d%s", prefix, mthIndex++, prepareNamePart(mth.getName()));
}
private void processPackageFull(PackageNode pkg, String fullName) {
@@ -1,6 +1,7 @@
package jadx.core.dex.attributes.nodes;
import java.util.List;
import java.util.SortedSet;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
@@ -17,9 +18,9 @@ public class MethodOverrideAttr implements IAttribute {
/**
* All method nodes from override hierarchy. Current method included.
*/
private List<MethodNode> relatedMthNodes;
private SortedSet<MethodNode> relatedMthNodes;
public MethodOverrideAttr(List<IMethodDetails> overrideList, List<MethodNode> relatedMthNodes) {
public MethodOverrideAttr(List<IMethodDetails> overrideList, SortedSet<MethodNode> relatedMthNodes) {
this.overrideList = overrideList;
this.relatedMthNodes = relatedMthNodes;
}
@@ -36,11 +37,11 @@ public class MethodOverrideAttr implements IAttribute {
this.overrideList = overrideList;
}
public List<MethodNode> getRelatedMthNodes() {
public SortedSet<MethodNode> getRelatedMthNodes() {
return relatedMthNodes;
}
public void setRelatedMthNodes(List<MethodNode> relatedMthNodes) {
public void setRelatedMthNodes(SortedSet<MethodNode> relatedMthNodes) {
this.relatedMthNodes = relatedMthNodes;
}
@@ -2,16 +2,18 @@ package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.clsp.ClspClass;
import jadx.core.clsp.ClspMethod;
@@ -39,9 +41,20 @@ import jadx.core.utils.exceptions.JadxException;
}
)
public class OverrideMethodVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(OverrideMethodVisitor.class);
@Override
public boolean visit(ClassNode cls) throws JadxException {
public void init(RootNode root) throws JadxException {
long startTime = System.currentTimeMillis();
for (ClassNode cls : root.getClasses()) {
processCls(cls);
}
if (LOG.isDebugEnabled()) {
LOG.debug("OverrideMethod pass time: {}ms", System.currentTimeMillis() - startTime);
}
}
public boolean processCls(ClassNode cls) {
List<ArgType> superTypes = collectSuperTypes(cls);
if (!superTypes.isEmpty()) {
for (MethodNode mth : cls.getMethods()) {
@@ -111,7 +124,8 @@ public class OverrideMethodVisitor extends AbstractVisitor {
}
@Nullable
private MethodOverrideAttr buildOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList, @Nullable MethodOverrideAttr attr) {
private MethodOverrideAttr buildOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList,
@Nullable MethodOverrideAttr attr) {
if (overrideList.isEmpty() && attr == null) {
return null;
}
@@ -129,21 +143,36 @@ public class OverrideMethodVisitor extends AbstractVisitor {
private MethodOverrideAttr applyOverrideAttr(MethodNode mth, List<IMethodDetails> overrideList, boolean update) {
// don't rename method if override list contains not resolved method
boolean dontRename = overrideList.stream().anyMatch(m -> !(m instanceof MethodNode));
SortedSet<MethodNode> relatedMethods = null;
List<MethodNode> mthNodes = getMethodNodes(mth, overrideList);
if (update) {
// merge related methods from all override attributes
Set<MethodNode> relatedMthSet = new HashSet<>(mthNodes);
for (MethodNode mthNode : mthNodes) {
MethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);
if (ovrdAttr != null) {
relatedMthSet.addAll(ovrdAttr.getRelatedMthNodes());
// use one of already allocated sets
relatedMethods = ovrdAttr.getRelatedMthNodes();
break;
}
}
if (relatedMthSet.size() != mthNodes.size()) {
mthNodes = new ArrayList<>(relatedMthSet);
Collections.sort(mthNodes);
if (relatedMethods != null) {
relatedMethods.addAll(mthNodes);
} else {
relatedMethods = new TreeSet<>(mthNodes);
}
for (MethodNode mthNode : mthNodes) {
MethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);
if (ovrdAttr != null) {
SortedSet<MethodNode> set = ovrdAttr.getRelatedMthNodes();
if (relatedMethods != set) {
relatedMethods.addAll(set);
}
}
}
} else {
relatedMethods = new TreeSet<>(mthNodes);
}
int depth = 0;
for (MethodNode mthNode : mthNodes) {
if (dontRename) {
@@ -157,26 +186,25 @@ public class OverrideMethodVisitor extends AbstractVisitor {
if (update) {
MethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE);
if (ovrdAttr != null) {
ovrdAttr.setRelatedMthNodes(mthNodes);
ovrdAttr.setRelatedMthNodes(relatedMethods);
continue;
}
}
mthNode.addAttr(new MethodOverrideAttr(Utils.listTail(overrideList, depth), mthNodes));
mthNode.addAttr(new MethodOverrideAttr(Utils.listTail(overrideList, depth), relatedMethods));
depth++;
}
return new MethodOverrideAttr(overrideList, mthNodes);
return new MethodOverrideAttr(overrideList, relatedMethods);
}
@NotNull
private List<MethodNode> getMethodNodes(MethodNode mth, List<IMethodDetails> overrideList) {
ArrayList<MethodNode> list = new ArrayList<>();
List<MethodNode> list = new ArrayList<>(1 + overrideList.size());
list.add(mth);
for (IMethodDetails md : overrideList) {
if (md instanceof MethodNode) {
list.add((MethodNode) md);
}
}
list.trimToSize();
return list;
}
@@ -7,6 +7,8 @@ import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.core.Consts;
@@ -24,6 +26,7 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
public class RenameVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(RenameVisitor.class);
@Override
public void init(RootNode root) {
@@ -31,6 +34,14 @@ public class RenameVisitor extends AbstractVisitor {
if (inputFiles.isEmpty()) {
return;
}
long startTime = System.currentTimeMillis();
process(root);
if (LOG.isDebugEnabled()) {
LOG.debug("Rename pass time: {}ms", System.currentTimeMillis() - startTime);
}
}
private void process(RootNode root) {
Deobfuscator deobfuscator = new Deobfuscator(root);
JadxArgs args = root.getArgs();
if (args.isDeobfuscationOn()) {
@@ -6,8 +6,6 @@ import org.slf4j.LoggerFactory;
import jadx.api.plugins.input.data.ICodeReader;
import jadx.api.plugins.input.insns.InsnData;
import jadx.api.plugins.input.insns.Opcode;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
@@ -67,12 +65,6 @@ public class UsageInfoVisitor extends AbstractVisitor {
} catch (Exception e) {
mth.addError("Dependency scan failed", e);
}
MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);
if (overrideAttr != null) {
for (MethodNode relatedMthNode : overrideAttr.getRelatedMthNodes()) {
usageInfo.methodUse(relatedMthNode, mth);
}
}
}
private static void processInstructions(MethodNode mth, UsageInfo usageInfo) {
@@ -37,6 +37,10 @@ public class JMethod extends JNode {
return mth;
}
public JavaMethod getJavaMethod() {
return mth;
}
@Override
public JClass getJParent() {
return jParent;
@@ -188,6 +188,9 @@ public class RenameDialog extends JDialog {
if (javaNode != null) {
toUpdate.add(javaNode);
toUpdate.addAll(javaNode.getUseIn());
if (node instanceof JMethod) {
toUpdate.addAll(((JMethod) node).getJavaMethod().getOverrideRelatedMethods());
}
} else if (node instanceof JPackage) {
processPackage(toUpdate);
} else {