Skip to content

Commit 0be5b2c

Browse files
committed
refactor(tests): add debug checks switch to jadx args
1 parent c94201b commit 0be5b2c

File tree

11 files changed

+106
-35
lines changed

11 files changed

+106
-35
lines changed

jadx-core/src/main/java/jadx/api/JadxArgs.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ public enum UseKotlinMethodsForVarNames {
168168
*/
169169
private boolean skipFilesSave = false;
170170

171+
/**
172+
* Run additional expensive checks to verify internal invariants and info integrity
173+
*/
174+
private boolean runDebugChecks = false;
175+
171176
private Map<String, String> pluginOptions = new HashMap<>();
172177

173178
private JadxPluginLoader pluginLoader = new JadxBasePluginLoader();
@@ -685,6 +690,14 @@ public void setSkipFilesSave(boolean skipFilesSave) {
685690
this.skipFilesSave = skipFilesSave;
686691
}
687692

693+
public boolean isRunDebugChecks() {
694+
return runDebugChecks;
695+
}
696+
697+
public void setRunDebugChecks(boolean runDebugChecks) {
698+
this.runDebugChecks = runDebugChecks;
699+
}
700+
688701
public Map<String, String> getPluginOptions() {
689702
return pluginOptions;
690703
}

jadx-core/src/main/java/jadx/core/ProcessClass.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.slf4j.LoggerFactory;
99

1010
import jadx.api.ICodeInfo;
11-
import jadx.api.JadxArgs;
1211
import jadx.api.impl.SimpleCodeInfo;
1312
import jadx.core.codegen.CodeGen;
1413
import jadx.core.dex.attributes.AFlag;
@@ -32,8 +31,8 @@ public class ProcessClass {
3231

3332
private final List<IDexTreeVisitor> passes;
3433

35-
public ProcessClass(JadxArgs args) {
36-
this.passes = Jadx.getPassesList(args);
34+
public ProcessClass(List<IDexTreeVisitor> passesList) {
35+
this.passes = passesList;
3736
}
3837

3938
@Nullable

jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import jadx.api.plugins.input.data.impl.ListConsumer;
3838
import jadx.api.usage.IUsageInfoData;
3939
import jadx.core.Consts;
40+
import jadx.core.Jadx;
4041
import jadx.core.ProcessClass;
4142
import jadx.core.dex.attributes.AFlag;
4243
import jadx.core.dex.attributes.AType;
@@ -324,7 +325,7 @@ public ICodeInfo decompileWithMode(DecompilationMode mode) {
324325
try {
325326
unload();
326327
args.setDecompilationMode(mode);
327-
ProcessClass process = new ProcessClass(args);
328+
ProcessClass process = new ProcessClass(Jadx.getPassesList(args));
328329
process.initPasses(root);
329330
return process.generateCode(this);
330331
} finally {

jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import jadx.core.dex.visitors.typeinference.TypeUpdate;
4949
import jadx.core.export.GradleInfoStorage;
5050
import jadx.core.utils.CacheStorage;
51+
import jadx.core.utils.DebugChecks;
5152
import jadx.core.utils.ErrorsCounter;
5253
import jadx.core.utils.PassMerge;
5354
import jadx.core.utils.StringUtils;
@@ -64,10 +65,6 @@ public class RootNode {
6465
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
6566

6667
private final JadxArgs args;
67-
private final List<IDexTreeVisitor> preDecompilePasses;
68-
private final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();
69-
70-
private final ProcessClass processClasses;
7168
private final ErrorsCounter errorsCounter = new ErrorsCounter();
7269
private final StringUtils stringUtils;
7370
private final ConstStorage constValues;
@@ -78,6 +75,7 @@ public class RootNode {
7875
private final TypeUtils typeUtils;
7976
private final AttributeStorage attributes = new AttributeStorage();
8077

78+
private final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();
8179
private final GradleInfoStorage gradleInfoStorage = new GradleInfoStorage();
8280

8381
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
@@ -87,11 +85,12 @@ public class RootNode {
8785
private final Map<String, PackageNode> pkgMap = new HashMap<>();
8886
private final List<PackageNode> packages = new ArrayList<>();
8987

88+
private List<IDexTreeVisitor> preDecompilePasses;
89+
private ProcessClass processClasses;
90+
9091
private ClspGraph clsp;
91-
@Nullable
92-
private String appPackage;
93-
@Nullable
94-
private ClassNode appResClass;
92+
private @Nullable String appPackage;
93+
private @Nullable ClassNode appResClass;
9594

9695
/**
9796
* Optional decompiler reference
@@ -101,7 +100,7 @@ public class RootNode {
101100
public RootNode(JadxArgs args) {
102101
this.args = args;
103102
this.preDecompilePasses = Jadx.getPreDecompilePassesList();
104-
this.processClasses = new ProcessClass(args);
103+
this.processClasses = new ProcessClass(Jadx.getPassesList(args));
105104
this.stringUtils = new StringUtils(args);
106105
this.constValues = new ConstStorage(args);
107106
this.typeUpdate = new TypeUpdate(this);
@@ -320,6 +319,11 @@ public void mergePasses(Map<JadxPassType, List<JadxPass>> customPasses) {
320319
.merge(customPasses.get(JadxPreparePass.TYPE), p -> new PreparePassWrapper((JadxPreparePass) p));
321320
new PassMerge(processClasses.getPasses())
322321
.merge(customPasses.get(JadxDecompilePass.TYPE), p -> new DecompilePassWrapper((JadxDecompilePass) p));
322+
323+
if (args.isRunDebugChecks()) {
324+
preDecompilePasses = DebugChecks.insertPasses(preDecompilePasses);
325+
processClasses = new ProcessClass(DebugChecks.insertPasses(processClasses.getPasses()));
326+
}
323327
}
324328

325329
public void runPreDecompileStage() {

jadx-core/src/main/java/jadx/core/dex/visitors/DepthTraversal.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import jadx.core.dex.attributes.AType;
44
import jadx.core.dex.nodes.ClassNode;
55
import jadx.core.dex.nodes.MethodNode;
6-
import jadx.core.utils.DebugChecks;
76

87
public class DepthTraversal {
98

@@ -24,9 +23,6 @@ public static void visit(IDexTreeVisitor visitor, MethodNode mth) {
2423
return;
2524
}
2625
visitor.visit(mth);
27-
if (DebugChecks.checksEnabled) {
28-
DebugChecks.runChecksAfterVisitor(mth, visitor);
29-
}
3026
} catch (StackOverflowError | Exception e) {
3127
mth.addError(e.getClass().getSimpleName() + " in pass: " + visitor.getClass().getSimpleName(), e);
3228
}

jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ private DotGraphVisitor(boolean useRegions, boolean rawInsn) {
5757
this.rawInsn = rawInsn;
5858
}
5959

60+
@Override
61+
public String getName() {
62+
return "DotGraphVisitor";
63+
}
64+
6065
@Override
6166
public void visit(MethodNode mth) {
6267
if (mth.isNoCode()) {

jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@
6161
)
6262
public class PrepareForCodeGen extends AbstractVisitor {
6363

64+
@Override
65+
public String getName() {
66+
return "PrepareForCodeGen";
67+
}
68+
6469
@Override
6570
public boolean visit(ClassNode cls) throws JadxException {
6671
if (cls.root().getArgs().isDebugInfo()) {

jadx-core/src/main/java/jadx/core/utils/DebugChecks.java

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package jadx.core.utils;
22

33
import java.util.ArrayList;
4+
import java.util.HashSet;
45
import java.util.List;
6+
import java.util.Set;
57

68
import jadx.core.dex.attributes.AFlag;
79
import jadx.core.dex.attributes.AType;
810
import jadx.core.dex.attributes.nodes.PhiListAttr;
11+
import jadx.core.dex.instructions.IfNode;
912
import jadx.core.dex.instructions.InsnType;
1013
import jadx.core.dex.instructions.PhiInsn;
1114
import jadx.core.dex.instructions.args.InsnArg;
@@ -17,26 +20,37 @@
1720
import jadx.core.dex.nodes.InsnNode;
1821
import jadx.core.dex.nodes.MethodNode;
1922
import jadx.core.dex.visitors.IDexTreeVisitor;
20-
import jadx.core.dex.visitors.PrepareForCodeGen;
21-
import jadx.core.dex.visitors.rename.RenameVisitor;
2223
import jadx.core.utils.exceptions.JadxRuntimeException;
2324

2425
/**
25-
* Check invariants and information consistency for registers and SSA variables
26+
* Check invariants and information consistency for blocks, instructions, registers, SSA variables.
27+
* These checks are very expensive and executed only in tests.
2628
*/
2729
public class DebugChecks {
2830

29-
public static boolean /* not final! */ checksEnabled = false;
31+
private static final Set<String> IGNORE_CHECKS = new HashSet<>(List.of(
32+
"PrepareForCodeGen",
33+
"RenameVisitor",
34+
"DotGraphVisitor"));
3035

31-
public static void runChecksAfterVisitor(MethodNode mth, IDexTreeVisitor visitor) {
32-
Class<? extends IDexTreeVisitor> visitorCls = visitor.getClass();
33-
if (visitorCls == PrepareForCodeGen.class || visitorCls == RenameVisitor.class) {
34-
return;
36+
public static List<IDexTreeVisitor> insertPasses(List<IDexTreeVisitor> passes) {
37+
int size = passes.size();
38+
List<IDexTreeVisitor> list = new ArrayList<>(size * 2);
39+
for (IDexTreeVisitor pass : passes) {
40+
list.add(pass);
41+
String name = pass.getName();
42+
if (!IGNORE_CHECKS.contains(name)) {
43+
list.add(new DebugChecksPass(name));
44+
}
3545
}
46+
return list;
47+
}
48+
49+
public static void runChecksAfterVisitor(MethodNode mth, String visitor) {
3650
try {
3751
checkMethod(mth);
3852
} catch (Exception e) {
39-
throw new JadxRuntimeException("Debug check failed after visitor: " + visitorCls.getSimpleName(), e);
53+
throw new JadxRuntimeException("Debug check failed after visitor: " + visitor, e);
4054
}
4155
}
4256

@@ -71,6 +85,16 @@ private static void checkInsn(MethodNode mth, InsnNode insn) {
7185
for (RegisterArg arg : ternaryInsn.getCondition().getRegisterArgs()) {
7286
checkVar(mth, insn, arg);
7387
}
88+
} else if (insn instanceof IfNode) {
89+
IfNode ifNode = (IfNode) insn;
90+
checkBlock(mth, ifNode.getThenBlock());
91+
checkBlock(mth, ifNode.getElseBlock());
92+
}
93+
}
94+
95+
private static void checkBlock(MethodNode mth, BlockNode block) {
96+
if (!mth.getBasicBlocks().contains(block)) {
97+
throw new JadxRuntimeException("Block not registered in method: " + block);
7498
}
7599
}
76100

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package jadx.core.utils;
2+
3+
import jadx.core.dex.attributes.AType;
4+
import jadx.core.dex.nodes.MethodNode;
5+
import jadx.core.dex.visitors.AbstractVisitor;
6+
import jadx.core.utils.exceptions.JadxException;
7+
8+
public class DebugChecksPass extends AbstractVisitor {
9+
10+
private final String visitorName;
11+
12+
public DebugChecksPass(String visitorName) {
13+
this.visitorName = visitorName;
14+
}
15+
16+
@Override
17+
public String getName() {
18+
return "Checks-for-" + visitorName;
19+
}
20+
21+
@Override
22+
public void visit(MethodNode mth) throws JadxException {
23+
if (!mth.contains(AType.JADX_ERROR)) {
24+
try {
25+
DebugChecks.runChecksAfterVisitor(mth, visitorName);
26+
} catch (Throwable e) {
27+
mth.addError("Check error", e);
28+
}
29+
}
30+
}
31+
}

jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
import jadx.core.dex.nodes.ClassNode;
5252
import jadx.core.dex.nodes.MethodNode;
5353
import jadx.core.dex.nodes.RootNode;
54-
import jadx.core.utils.DebugChecks;
5554
import jadx.core.utils.Utils;
5655
import jadx.core.utils.exceptions.JadxRuntimeException;
5756
import jadx.core.utils.files.FileUtils;
@@ -62,9 +61,9 @@
6261
import jadx.tests.api.compiler.TestCompiler;
6362
import jadx.tests.api.utils.TestUtils;
6463

65-
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
6664
import static org.apache.commons.lang3.StringUtils.leftPad;
6765
import static org.apache.commons.lang3.StringUtils.rightPad;
66+
import static org.assertj.core.api.Assertions.assertThat;
6867
import static org.assertj.core.api.Assertions.fail;
6968

7069
public abstract class IntegrationTest extends TestUtils {
@@ -114,11 +113,6 @@ public abstract class IntegrationTest extends TestUtils {
114113
*/
115114
private boolean forceDecompiledCheck = false;
116115

117-
static {
118-
// enable debug checks
119-
DebugChecks.checksEnabled = true;
120-
}
121-
122116
protected JadxDecompiler jadxDecompiler;
123117

124118
@BeforeEach
@@ -137,6 +131,7 @@ public void init() {
137131
args.setCommentsLevel(CommentsLevel.DEBUG);
138132
args.setDeobfuscationOn(false);
139133
args.setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode.IGNORE);
134+
args.setRunDebugChecks(true);
140135

141136
// use the same values on all systems
142137
args.setFsCaseSensitive(false);

0 commit comments

Comments
 (0)