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 extends IDexTreeVisitor>[] runAfter() default {};
+
+ /**
+ * This visitor must be run before listed visitors
+ */
+ Class extends IDexTreeVisitor>[] 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 extends IDexTreeVisitor> cls : info.runBefore()) {
+ if (classList.indexOf(cls) < i) {
+ errors.add("Pass " + passName + " must be before " + cls.getSimpleName());
+ }
+ }
+ for (Class extends IDexTreeVisitor> cls : info.runAfter()) {
+ if (classList.indexOf(cls) > i) {
+ errors.add("Pass " + passName + " must be after " + cls.getSimpleName());
+ }
+ }
+ }
+ return errors;
+ }
+}