From b7ca898b772d7c0206c5b9fe8b2e810b899dcd69 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 31 Dec 2020 13:33:18 +0300 Subject: [PATCH] perf: improve processing of override related methods (#1072) --- .../src/main/java/jadx/api/JavaMethod.java | 11 ++++ .../java/jadx/core/deobf/Deobfuscator.java | 26 ++++----- .../attributes/nodes/MethodOverrideAttr.java | 9 +-- .../dex/visitors/OverrideMethodVisitor.java | 56 ++++++++++++++----- .../jadx/core/dex/visitors/RenameVisitor.java | 11 ++++ .../dex/visitors/usage/UsageInfoVisitor.java | 8 --- .../main/java/jadx/gui/treemodel/JMethod.java | 4 ++ .../main/java/jadx/gui/ui/RenameDialog.java | 3 + 8 files changed, 89 insertions(+), 39 deletions(-) diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index acfc6e345..d6bc3c73c 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -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 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(); } diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 843f73ae5..61d01f3fa 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -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) { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodOverrideAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodOverrideAttr.java index 3c9eb9a65..daceb9c0c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodOverrideAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/MethodOverrideAttr.java @@ -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 relatedMthNodes; + private SortedSet relatedMthNodes; - public MethodOverrideAttr(List overrideList, List relatedMthNodes) { + public MethodOverrideAttr(List overrideList, SortedSet relatedMthNodes) { this.overrideList = overrideList; this.relatedMthNodes = relatedMthNodes; } @@ -36,11 +37,11 @@ public class MethodOverrideAttr implements IAttribute { this.overrideList = overrideList; } - public List getRelatedMthNodes() { + public SortedSet getRelatedMthNodes() { return relatedMthNodes; } - public void setRelatedMthNodes(List relatedMthNodes) { + public void setRelatedMthNodes(SortedSet relatedMthNodes) { this.relatedMthNodes = relatedMthNodes; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java index 30c59c29d..5a46e9c04 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java @@ -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 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 overrideList, @Nullable MethodOverrideAttr attr) { + private MethodOverrideAttr buildOverrideAttr(MethodNode mth, List 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 overrideList, boolean update) { // don't rename method if override list contains not resolved method boolean dontRename = overrideList.stream().anyMatch(m -> !(m instanceof MethodNode)); + SortedSet relatedMethods = null; List mthNodes = getMethodNodes(mth, overrideList); if (update) { // merge related methods from all override attributes - Set 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 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 getMethodNodes(MethodNode mth, List overrideList) { - ArrayList list = new ArrayList<>(); + List 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; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index 478deaa83..bbb0b5320 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -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()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java index 19989c8be..28e7a3d5b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java @@ -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) { diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java index 04f724a26..bf0ca7ce9 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java @@ -37,6 +37,10 @@ public class JMethod extends JNode { return mth; } + public JavaMethod getJavaMethod() { + return mth; + } + @Override public JClass getJParent() { return jParent; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java index 62e1e9522..d0d8e8c7b 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java @@ -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 {