From 217737b3e85dc0980c9dffa989d3b3f2cec87c08 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 5 Apr 2015 15:01:11 +0300 Subject: [PATCH] core: add jadx visitors annotation for describe dependencies --- .../jadx/core/dex/visitors/ClassModifier.java | 6 ++ .../jadx/core/dex/visitors/JadxVisitor.java | 33 +++++++++ .../jadx/core/dex/visitors/ReSugarCode.java | 6 ++ .../core/dex/visitors/ssa/SSATransform.java | 7 ++ .../functional/JadxVisitorsOrderTest.java | 68 +++++++++++++++++++ 5 files changed, 120 insertions(+) create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/JadxVisitor.java create mode 100644 jadx-core/src/test/java/jadx/tests/functional/JadxVisitorsOrderTest.java diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index c94eca408..5a2960c05 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -22,6 +22,11 @@ import jadx.core.utils.exceptions.JadxException; import java.util.List; +@JadxVisitor( + name = "ClassModifier", + desc = "Remove synthetic classes, methods and fields", + runAfter = ModVisitor.class +) public class ClassModifier extends AbstractVisitor { @Override @@ -129,6 +134,7 @@ public class ClassModifier extends AbstractVisitor { if (af.isBridge() && af.isSynthetic() && !isMethodUniq(cls, mth)) { // TODO add more checks before method deletion mth.add(AFlag.DONT_GENERATE); + continue; } // remove synthetic constructor for inner non-static classes if (af.isSynthetic() && af.isConstructor() && mth.getBasicBlocks().size() == 2) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/JadxVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/JadxVisitor.java new file mode 100644 index 000000000..7036ae6ff --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/JadxVisitor.java @@ -0,0 +1,33 @@ +package jadx.core.dex.visitors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for describe dependencies of jadx visitors + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface JadxVisitor { + /** + * Visitor short name (identifier) + */ + String name(); + + /** + * Detailed visitor description + */ + String desc() default ""; + + /** + * This visitor must be run after listed visitors + */ + Class[] runAfter() default {}; + + /** + * This visitor must be run before listed visitors + */ + Class[] runBefore() default {}; +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java index a9f0876ce..de3996ade 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java @@ -29,7 +29,13 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@JadxVisitor( + name = "ReSugarCode", + desc = "Simplify synthetic or verbose code", + runAfter = CodeShrinker.class +) public class ReSugarCode extends AbstractVisitor { + private static final Logger LOG = LoggerFactory.getLogger(ReSugarCode.class); @Override diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java index fc487a0f2..8807aba16 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -12,6 +12,8 @@ import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.dex.visitors.JadxVisitor; +import jadx.core.dex.visitors.blocksmaker.BlockFinish; import jadx.core.utils.InsnList; import jadx.core.utils.InstructionRemover; import jadx.core.utils.exceptions.JadxException; @@ -25,6 +27,11 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +@JadxVisitor( + name = "SSATransform", + desc = "Calculate Single Side Assign (SSA) variables", + runAfter = BlockFinish.class +) public class SSATransform extends AbstractVisitor { @Override diff --git a/jadx-core/src/test/java/jadx/tests/functional/JadxVisitorsOrderTest.java b/jadx-core/src/test/java/jadx/tests/functional/JadxVisitorsOrderTest.java new file mode 100644 index 000000000..fad173352 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/functional/JadxVisitorsOrderTest.java @@ -0,0 +1,68 @@ +package jadx.tests.functional; + +import jadx.api.DefaultJadxArgs; +import jadx.core.Jadx; +import jadx.core.dex.visitors.IDexTreeVisitor; +import jadx.core.dex.visitors.JadxVisitor; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.hamcrest.Matchers.empty; +import static org.junit.Assert.assertThat; + +public class JadxVisitorsOrderTest { + + private static final Logger LOG = LoggerFactory.getLogger(JadxVisitorsOrderTest.class); + + @Test + public void testOrder() { + List passes = Jadx.getPassesList(new DefaultJadxArgs(), new File("out")); + + List errors = check(passes); + for (String str : errors) { + LOG.error(str); + } + assertThat(errors, empty()); + } + + private static List check(List passes) { + List classList = new ArrayList(passes.size()); + for (IDexTreeVisitor pass : passes) { + classList.add(pass.getClass()); + } + List errors = new ArrayList(); + + Set names = new HashSet(); + for (int i = 0; i < passes.size(); i++) { + IDexTreeVisitor pass = passes.get(i); + JadxVisitor info = pass.getClass().getAnnotation(JadxVisitor.class); + if (info == null) { + LOG.warn("No JadxVisitor annotation for visitor: {}", pass.getClass().getName()); + continue; + } + String passName = pass.getClass().getSimpleName(); + if (!names.add(passName)) { + errors.add("Visitor name conflict: " + passName + ", class: " + pass.getClass().getName()); + } + for (Class cls : info.runBefore()) { + if (classList.indexOf(cls) < i) { + errors.add("Pass " + passName + " must be before " + cls.getSimpleName()); + } + } + for (Class cls : info.runAfter()) { + if (classList.indexOf(cls) > i) { + errors.add("Pass " + passName + " must be after " + cls.getSimpleName()); + } + } + } + return errors; + } +}