mirror of
https://github.com/skylot/jadx.git
synced 2024-10-07 01:53:34 +00:00
refactor(tests): add debug checks switch to jadx args
This commit is contained in:
parent
c94201be4a
commit
0be5b2cea9
@ -168,6 +168,11 @@ public class JadxArgs implements Closeable {
|
||||
*/
|
||||
private boolean skipFilesSave = false;
|
||||
|
||||
/**
|
||||
* Run additional expensive checks to verify internal invariants and info integrity
|
||||
*/
|
||||
private boolean runDebugChecks = false;
|
||||
|
||||
private Map<String, String> pluginOptions = new HashMap<>();
|
||||
|
||||
private JadxPluginLoader pluginLoader = new JadxBasePluginLoader();
|
||||
@ -685,6 +690,14 @@ public class JadxArgs implements Closeable {
|
||||
this.skipFilesSave = skipFilesSave;
|
||||
}
|
||||
|
||||
public boolean isRunDebugChecks() {
|
||||
return runDebugChecks;
|
||||
}
|
||||
|
||||
public void setRunDebugChecks(boolean runDebugChecks) {
|
||||
this.runDebugChecks = runDebugChecks;
|
||||
}
|
||||
|
||||
public Map<String, String> getPluginOptions() {
|
||||
return pluginOptions;
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.codegen.CodeGen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
@ -32,8 +31,8 @@ public class ProcessClass {
|
||||
|
||||
private final List<IDexTreeVisitor> passes;
|
||||
|
||||
public ProcessClass(JadxArgs args) {
|
||||
this.passes = Jadx.getPassesList(args);
|
||||
public ProcessClass(List<IDexTreeVisitor> passesList) {
|
||||
this.passes = passesList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -37,6 +37,7 @@ import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
|
||||
import jadx.api.plugins.input.data.impl.ListConsumer;
|
||||
import jadx.api.usage.IUsageInfoData;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
@ -324,7 +325,7 @@ public class ClassNode extends NotificationAttrNode
|
||||
try {
|
||||
unload();
|
||||
args.setDecompilationMode(mode);
|
||||
ProcessClass process = new ProcessClass(args);
|
||||
ProcessClass process = new ProcessClass(Jadx.getPassesList(args));
|
||||
process.initPasses(root);
|
||||
return process.generateCode(this);
|
||||
} finally {
|
||||
|
@ -48,6 +48,7 @@ import jadx.core.dex.visitors.typeinference.TypeCompare;
|
||||
import jadx.core.dex.visitors.typeinference.TypeUpdate;
|
||||
import jadx.core.export.GradleInfoStorage;
|
||||
import jadx.core.utils.CacheStorage;
|
||||
import jadx.core.utils.DebugChecks;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.PassMerge;
|
||||
import jadx.core.utils.StringUtils;
|
||||
@ -64,10 +65,6 @@ public class RootNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
|
||||
|
||||
private final JadxArgs args;
|
||||
private final List<IDexTreeVisitor> preDecompilePasses;
|
||||
private final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();
|
||||
|
||||
private final ProcessClass processClasses;
|
||||
private final ErrorsCounter errorsCounter = new ErrorsCounter();
|
||||
private final StringUtils stringUtils;
|
||||
private final ConstStorage constValues;
|
||||
@ -78,6 +75,7 @@ public class RootNode {
|
||||
private final TypeUtils typeUtils;
|
||||
private final AttributeStorage attributes = new AttributeStorage();
|
||||
|
||||
private final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();
|
||||
private final GradleInfoStorage gradleInfoStorage = new GradleInfoStorage();
|
||||
|
||||
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
|
||||
@ -87,11 +85,12 @@ public class RootNode {
|
||||
private final Map<String, PackageNode> pkgMap = new HashMap<>();
|
||||
private final List<PackageNode> packages = new ArrayList<>();
|
||||
|
||||
private List<IDexTreeVisitor> preDecompilePasses;
|
||||
private ProcessClass processClasses;
|
||||
|
||||
private ClspGraph clsp;
|
||||
@Nullable
|
||||
private String appPackage;
|
||||
@Nullable
|
||||
private ClassNode appResClass;
|
||||
private @Nullable String appPackage;
|
||||
private @Nullable ClassNode appResClass;
|
||||
|
||||
/**
|
||||
* Optional decompiler reference
|
||||
@ -101,7 +100,7 @@ public class RootNode {
|
||||
public RootNode(JadxArgs args) {
|
||||
this.args = args;
|
||||
this.preDecompilePasses = Jadx.getPreDecompilePassesList();
|
||||
this.processClasses = new ProcessClass(args);
|
||||
this.processClasses = new ProcessClass(Jadx.getPassesList(args));
|
||||
this.stringUtils = new StringUtils(args);
|
||||
this.constValues = new ConstStorage(args);
|
||||
this.typeUpdate = new TypeUpdate(this);
|
||||
@ -320,6 +319,11 @@ public class RootNode {
|
||||
.merge(customPasses.get(JadxPreparePass.TYPE), p -> new PreparePassWrapper((JadxPreparePass) p));
|
||||
new PassMerge(processClasses.getPasses())
|
||||
.merge(customPasses.get(JadxDecompilePass.TYPE), p -> new DecompilePassWrapper((JadxDecompilePass) p));
|
||||
|
||||
if (args.isRunDebugChecks()) {
|
||||
preDecompilePasses = DebugChecks.insertPasses(preDecompilePasses);
|
||||
processClasses = new ProcessClass(DebugChecks.insertPasses(processClasses.getPasses()));
|
||||
}
|
||||
}
|
||||
|
||||
public void runPreDecompileStage() {
|
||||
|
@ -3,7 +3,6 @@ package jadx.core.dex.visitors;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.DebugChecks;
|
||||
|
||||
public class DepthTraversal {
|
||||
|
||||
@ -24,9 +23,6 @@ public class DepthTraversal {
|
||||
return;
|
||||
}
|
||||
visitor.visit(mth);
|
||||
if (DebugChecks.checksEnabled) {
|
||||
DebugChecks.runChecksAfterVisitor(mth, visitor);
|
||||
}
|
||||
} catch (StackOverflowError | Exception e) {
|
||||
mth.addError(e.getClass().getSimpleName() + " in pass: " + visitor.getClass().getSimpleName(), e);
|
||||
}
|
||||
|
@ -57,6 +57,11 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
this.rawInsn = rawInsn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "DotGraphVisitor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
if (mth.isNoCode()) {
|
||||
|
@ -61,6 +61,11 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
)
|
||||
public class PrepareForCodeGen extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "PrepareForCodeGen";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws JadxException {
|
||||
if (cls.root().getArgs().isDebugInfo()) {
|
||||
|
@ -1,11 +1,14 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@ -17,26 +20,37 @@ import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.PrepareForCodeGen;
|
||||
import jadx.core.dex.visitors.rename.RenameVisitor;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
/**
|
||||
* Check invariants and information consistency for registers and SSA variables
|
||||
* Check invariants and information consistency for blocks, instructions, registers, SSA variables.
|
||||
* These checks are very expensive and executed only in tests.
|
||||
*/
|
||||
public class DebugChecks {
|
||||
|
||||
public static boolean /* not final! */ checksEnabled = false;
|
||||
private static final Set<String> IGNORE_CHECKS = new HashSet<>(List.of(
|
||||
"PrepareForCodeGen",
|
||||
"RenameVisitor",
|
||||
"DotGraphVisitor"));
|
||||
|
||||
public static void runChecksAfterVisitor(MethodNode mth, IDexTreeVisitor visitor) {
|
||||
Class<? extends IDexTreeVisitor> visitorCls = visitor.getClass();
|
||||
if (visitorCls == PrepareForCodeGen.class || visitorCls == RenameVisitor.class) {
|
||||
return;
|
||||
public static List<IDexTreeVisitor> insertPasses(List<IDexTreeVisitor> passes) {
|
||||
int size = passes.size();
|
||||
List<IDexTreeVisitor> list = new ArrayList<>(size * 2);
|
||||
for (IDexTreeVisitor pass : passes) {
|
||||
list.add(pass);
|
||||
String name = pass.getName();
|
||||
if (!IGNORE_CHECKS.contains(name)) {
|
||||
list.add(new DebugChecksPass(name));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void runChecksAfterVisitor(MethodNode mth, String visitor) {
|
||||
try {
|
||||
checkMethod(mth);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Debug check failed after visitor: " + visitorCls.getSimpleName(), e);
|
||||
throw new JadxRuntimeException("Debug check failed after visitor: " + visitor, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +85,16 @@ public class DebugChecks {
|
||||
for (RegisterArg arg : ternaryInsn.getCondition().getRegisterArgs()) {
|
||||
checkVar(mth, insn, arg);
|
||||
}
|
||||
} else if (insn instanceof IfNode) {
|
||||
IfNode ifNode = (IfNode) insn;
|
||||
checkBlock(mth, ifNode.getThenBlock());
|
||||
checkBlock(mth, ifNode.getElseBlock());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkBlock(MethodNode mth, BlockNode block) {
|
||||
if (!mth.getBasicBlocks().contains(block)) {
|
||||
throw new JadxRuntimeException("Block not registered in method: " + block);
|
||||
}
|
||||
}
|
||||
|
||||
|
31
jadx-core/src/main/java/jadx/core/utils/DebugChecksPass.java
Normal file
31
jadx-core/src/main/java/jadx/core/utils/DebugChecksPass.java
Normal file
@ -0,0 +1,31 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
public class DebugChecksPass extends AbstractVisitor {
|
||||
|
||||
private final String visitorName;
|
||||
|
||||
public DebugChecksPass(String visitorName) {
|
||||
this.visitorName = visitorName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Checks-for-" + visitorName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (!mth.contains(AType.JADX_ERROR)) {
|
||||
try {
|
||||
DebugChecks.runChecksAfterVisitor(mth, visitorName);
|
||||
} catch (Throwable e) {
|
||||
mth.addError("Check error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,6 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.DebugChecks;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
@ -62,9 +61,9 @@ import jadx.tests.api.compiler.JavaUtils;
|
||||
import jadx.tests.api.compiler.TestCompiler;
|
||||
import jadx.tests.api.utils.TestUtils;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
import static org.apache.commons.lang3.StringUtils.leftPad;
|
||||
import static org.apache.commons.lang3.StringUtils.rightPad;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
public abstract class IntegrationTest extends TestUtils {
|
||||
@ -114,11 +113,6 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
*/
|
||||
private boolean forceDecompiledCheck = false;
|
||||
|
||||
static {
|
||||
// enable debug checks
|
||||
DebugChecks.checksEnabled = true;
|
||||
}
|
||||
|
||||
protected JadxDecompiler jadxDecompiler;
|
||||
|
||||
@BeforeEach
|
||||
@ -137,6 +131,7 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
args.setCommentsLevel(CommentsLevel.DEBUG);
|
||||
args.setDeobfuscationOn(false);
|
||||
args.setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode.IGNORE);
|
||||
args.setRunDebugChecks(true);
|
||||
|
||||
// use the same values on all systems
|
||||
args.setFsCaseSensitive(false);
|
||||
|
@ -19,7 +19,6 @@ import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.DebugChecks;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.tests.api.utils.TestUtils;
|
||||
|
||||
@ -37,7 +36,6 @@ public abstract class BaseExternalTest extends TestUtils {
|
||||
}
|
||||
|
||||
protected JadxArgs prepare(File input) {
|
||||
DebugChecks.checksEnabled = false;
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.getInputFiles().add(input);
|
||||
args.setOutDir(new File("../jadx-external-tests-tmp"));
|
||||
|
Loading…
Reference in New Issue
Block a user