core: show all decompilation errors in code comments (#313)
This commit is contained in:
@@ -10,31 +10,36 @@ public class JadxCLI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
int result = 0;
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (jadxArgs.processArgs(args)) {
|
||||
processAndSave(jadxArgs);
|
||||
result = processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("jadx error: {}", e.getMessage(), e);
|
||||
System.exit(1);
|
||||
result = 1;
|
||||
} finally {
|
||||
System.exit(result);
|
||||
}
|
||||
}
|
||||
|
||||
static void processAndSave(JadxCLIArgs inputArgs) {
|
||||
static int processAndSave(JadxCLIArgs inputArgs) {
|
||||
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
|
||||
try {
|
||||
jadx.load();
|
||||
} catch (JadxArgsValidateException e) {
|
||||
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||
System.exit(1);
|
||||
return 1;
|
||||
}
|
||||
jadx.save();
|
||||
if (jadx.getErrorsCount() != 0) {
|
||||
int errorsCount = jadx.getErrorsCount();
|
||||
if (errorsCount != 0) {
|
||||
jadx.printErrorsReport();
|
||||
LOG.error("finished with errors");
|
||||
} else {
|
||||
LOG.info("done");
|
||||
}
|
||||
return errorsCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,13 @@ public final class JadxDecompiler {
|
||||
return root.getErrorsCounter().getErrorCount();
|
||||
}
|
||||
|
||||
public int getWarnsCount() {
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
return root.getErrorsCounter().getWarnsCount();
|
||||
}
|
||||
|
||||
public void printErrorsReport() {
|
||||
if (root == null) {
|
||||
return;
|
||||
|
||||
@@ -18,6 +18,8 @@ import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JadxWarn;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
@@ -253,6 +255,7 @@ public class ClassGen {
|
||||
if (code.getLine() != clsDeclLine) {
|
||||
code.newLine();
|
||||
}
|
||||
int savedIndent = code.getIndent();
|
||||
try {
|
||||
addMethod(code, mth);
|
||||
} catch (Exception e) {
|
||||
@@ -260,6 +263,7 @@ public class ClassGen {
|
||||
code.newLine().add(ErrorsCounter.methodError(mth, "Method generation error", e));
|
||||
code.newLine().add(Utils.getStackTrace(e));
|
||||
code.newLine().add("*/");
|
||||
code.setIndent(savedIndent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,12 +296,11 @@ public class ClassGen {
|
||||
}
|
||||
code.add(';');
|
||||
} else {
|
||||
insertDecompilationProblems(code, mth);
|
||||
boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
|
||||
if (badCode) {
|
||||
code.startLine("/* JADX WARNING: inconsistent code. */");
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
ErrorsCounter.methodError(mth, "Inconsistent code");
|
||||
if (showInconsistentCode) {
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
mth.remove(AFlag.INCONSISTENT_CODE);
|
||||
badCode = false;
|
||||
}
|
||||
@@ -324,6 +327,26 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void insertDecompilationProblems(CodeWriter code, MethodNode mth) {
|
||||
List<JadxError> errors = mth.getAll(AType.JADX_ERROR);
|
||||
List<JadxWarn> warns = mth.getAll(AType.JADX_WARN);
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(err -> {
|
||||
code.startLine("/* JADX ERROR: ").add(err.getError());
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.incIndent();
|
||||
Utils.appendStackTrace(code, cause);
|
||||
code.decIndent();
|
||||
}
|
||||
code.add("*/");
|
||||
});
|
||||
}
|
||||
if (!warns.isEmpty()) {
|
||||
warns.forEach(warn -> code.startLine("/* JADX WARNING: ").add(warn.getWarn()).add(" */"));
|
||||
}
|
||||
}
|
||||
|
||||
private void addFields(CodeWriter code) throws CodegenException {
|
||||
addEnumFields(code);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
|
||||
@@ -191,6 +191,11 @@ public class CodeWriter {
|
||||
return indent;
|
||||
}
|
||||
|
||||
public void setIndent(int indent) {
|
||||
this.indent = indent;
|
||||
updateIndent();
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class ConditionGen extends InsnGen {
|
||||
wrap(code, firstArg);
|
||||
return;
|
||||
}
|
||||
ErrorsCounter.methodError(mth, "Unsupported boolean condition " + op.getSymbol());
|
||||
ErrorsCounter.methodWarn(mth, "Unsupported boolean condition " + op.getSymbol());
|
||||
}
|
||||
|
||||
addArg(code, firstArg, isArgWrapNeeded(firstArg));
|
||||
|
||||
@@ -580,7 +580,7 @@ public class InsnGen {
|
||||
// anonymous class construction
|
||||
if (cls.contains(AFlag.DONT_GENERATE)) {
|
||||
code.add("/* anonymous class already generated */");
|
||||
ErrorsCounter.methodError(mth, "Anonymous class already generated: " + cls);
|
||||
ErrorsCounter.methodWarn(mth, "Anonymous class already generated: " + cls);
|
||||
return;
|
||||
}
|
||||
ArgType parent;
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -23,7 +22,6 @@ import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.FallbackModeVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
@@ -108,7 +106,7 @@ public class MethodGen {
|
||||
} else if (args.size() > 2) {
|
||||
args = args.subList(2, args.size());
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth,
|
||||
LOG.warn(ErrorsCounter.formatMsg(mth,
|
||||
"Incorrect number of args for enum constructor: " + args.size()
|
||||
+ " (expected >= 2)"
|
||||
));
|
||||
@@ -143,7 +141,7 @@ public class MethodGen {
|
||||
classGen.useType(argsCode, elType);
|
||||
argsCode.add("...");
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Last argument in varargs method not array"));
|
||||
LOG.warn(ErrorsCounter.formatMsg(mth, "Last argument in varargs method not array"));
|
||||
classGen.useType(argsCode, arg.getType());
|
||||
}
|
||||
} else {
|
||||
@@ -163,17 +161,6 @@ public class MethodGen {
|
||||
if (mth.contains(AType.JADX_ERROR)
|
||||
|| mth.contains(AFlag.INCONSISTENT_CODE)
|
||||
|| mth.getRegion() == null) {
|
||||
JadxErrorAttr err = mth.get(AType.JADX_ERROR);
|
||||
if (err != null) {
|
||||
code.startLine("/* JADX: method processing error */");
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.newLine();
|
||||
code.add("/*");
|
||||
code.newLine().add("Error: ").addMultiLine(Utils.getStackTrace(cause));
|
||||
code.add("*/");
|
||||
}
|
||||
}
|
||||
code.startLine("/*");
|
||||
addFallbackMethodCode(code);
|
||||
code.startLine("*/");
|
||||
@@ -189,19 +176,14 @@ public class MethodGen {
|
||||
|
||||
public void addFallbackMethodCode(CodeWriter code) {
|
||||
if (mth.getInstructions() == null) {
|
||||
JadxErrorAttr errorAttr = mth.get(AType.JADX_ERROR);
|
||||
if (errorAttr == null
|
||||
|| errorAttr.getCause() == null
|
||||
|| !errorAttr.getCause().getClass().equals(DecodeException.class)) {
|
||||
// load original instructions
|
||||
try {
|
||||
mth.load();
|
||||
DepthTraversal.visit(new FallbackModeVisitor(), mth);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error reload instructions in fallback mode:", e);
|
||||
code.startLine("// Can't load method instructions: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
// load original instructions
|
||||
try {
|
||||
mth.load();
|
||||
DepthTraversal.visit(new FallbackModeVisitor(), mth);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error reload instructions in fallback mode:", e);
|
||||
code.startLine("// Can't load method instructions: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
InsnNode[] insnArr = mth.getInstructions();
|
||||
@@ -251,5 +233,4 @@ public class MethodGen {
|
||||
public static String getLabelName(int offset) {
|
||||
return "L_" + InsnUtils.formatOffset(offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Set;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
@@ -136,11 +137,11 @@ public class NameGen {
|
||||
private String makeNameForType(ArgType type) {
|
||||
if (type.isPrimitive()) {
|
||||
return makeNameForPrimitive(type);
|
||||
} else if (type.isArray()) {
|
||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||
} else {
|
||||
return makeNameForObject(type);
|
||||
}
|
||||
if (type.isArray()) {
|
||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||
}
|
||||
return makeNameForObject(type);
|
||||
}
|
||||
|
||||
private static String makeNameForPrimitive(ArgType type) {
|
||||
|
||||
@@ -153,7 +153,7 @@ public class RegionGen extends InsnGen {
|
||||
if (header != null) {
|
||||
List<InsnNode> headerInsns = header.getInstructions();
|
||||
if (headerInsns.size() > 1) {
|
||||
ErrorsCounter.methodError(mth, "Found not inlined instructions from loop header");
|
||||
ErrorsCounter.methodWarn(mth, "Found not inlined instructions from loop header");
|
||||
int last = headerInsns.size() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
InsnNode insn = headerInsns.get(i);
|
||||
|
||||
@@ -9,7 +9,8 @@ import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JadxWarn;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
@@ -33,13 +34,15 @@ public class AType<T extends IAttribute> {
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<>();
|
||||
public static final AType<AttrList<EdgeInsnAttr>> EDGE_INSN = new AType<>();
|
||||
|
||||
public static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>();
|
||||
public static final AType<AttrList<JadxWarn>> JADX_WARN = new AType<>();
|
||||
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<>();
|
||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
||||
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
@@ -8,7 +8,7 @@ import jadx.core.utils.Utils;
|
||||
public class AttrList<T> implements IAttribute {
|
||||
|
||||
private final AType<AttrList<T>> type;
|
||||
private final List<T> list = new LinkedList<>();
|
||||
private final List<T> list = new ArrayList<>();
|
||||
|
||||
public AttrList(AType<AttrList<T>> type) {
|
||||
this.type = type;
|
||||
|
||||
+17
-13
@@ -1,33 +1,38 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class JadxErrorAttr implements IAttribute {
|
||||
public class JadxError {
|
||||
|
||||
private final String error;
|
||||
private final Throwable cause;
|
||||
|
||||
public JadxErrorAttr(Throwable cause) {
|
||||
public JadxError(Throwable cause) {
|
||||
this(null, cause);
|
||||
}
|
||||
|
||||
public JadxError(String error, Throwable cause) {
|
||||
this.error = error;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<JadxErrorAttr> getType() {
|
||||
return AType.JADX_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("JadxError: ");
|
||||
if (cause == null) {
|
||||
str.append("null");
|
||||
} else {
|
||||
if (error != null) {
|
||||
str.append(error);
|
||||
str.append(' ');
|
||||
}
|
||||
if (cause != null) {
|
||||
str.append(cause.getClass());
|
||||
str.append(":");
|
||||
str.append(cause.getMessage());
|
||||
@@ -36,5 +41,4 @@ public class JadxErrorAttr implements IAttribute {
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class JadxWarn {
|
||||
|
||||
private final String warn;
|
||||
|
||||
public JadxWarn(String warn) {
|
||||
this.warn = Objects.requireNonNull(warn);
|
||||
}
|
||||
|
||||
public String getWarn() {
|
||||
return warn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JadxWarn: " + warn;
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (sVar != null) {
|
||||
if (sVar != null && name != null) {
|
||||
sVar.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
@@ -35,7 +33,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
// immediate dominator
|
||||
private BlockNode idom;
|
||||
// blocks on which dominates this block
|
||||
private List<BlockNode> dominatesOn = Collections.emptyList();
|
||||
private List<BlockNode> dominatesOn = new ArrayList<>(3);
|
||||
|
||||
public BlockNode(int id, int offset) {
|
||||
this.id = id;
|
||||
@@ -84,7 +82,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
if (sucList.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> toRemove = new LinkedList<>();
|
||||
List<BlockNode> toRemove = new ArrayList<>(sucList.size());
|
||||
for (BlockNode b : sucList) {
|
||||
if (BlockUtils.isBlockMustBeCleared(b)) {
|
||||
toRemove.add(b);
|
||||
@@ -159,9 +157,6 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
|
||||
public void addDominatesOn(BlockNode block) {
|
||||
if (dominatesOn.isEmpty()) {
|
||||
dominatesOn = new LinkedList<>();
|
||||
}
|
||||
dominatesOn.add(block);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
@@ -248,8 +247,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
try {
|
||||
mth.load();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Method load error: {}", mth, e);
|
||||
mth.addAttr(new JadxErrorAttr(e));
|
||||
mth.addError("Method load error", e);
|
||||
}
|
||||
}
|
||||
for (ClassNode innerCls : getInnerClasses()) {
|
||||
@@ -418,6 +416,11 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return dex.root();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "class";
|
||||
}
|
||||
|
||||
public String getRawName() {
|
||||
return clsInfo.getRawName();
|
||||
}
|
||||
|
||||
@@ -235,6 +235,11 @@ public class DexNode implements IDexNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "dex";
|
||||
}
|
||||
|
||||
public int getDexId() {
|
||||
return dexId;
|
||||
}
|
||||
@@ -243,5 +248,4 @@ public class DexNode implements IDexNode {
|
||||
public String toString() {
|
||||
return "DEX: " + file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package jadx.core.dex.nodes;
|
||||
|
||||
public interface IDexNode {
|
||||
|
||||
String typeName();
|
||||
|
||||
DexNode dex();
|
||||
|
||||
RootNode root();
|
||||
|
||||
@@ -39,6 +39,7 @@ import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -605,6 +606,19 @@ public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
return dex().root();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "method";
|
||||
}
|
||||
|
||||
public void addWarn(String errStr) {
|
||||
ErrorsCounter.methodWarn(this, errStr);
|
||||
}
|
||||
|
||||
public void addError(String errStr, Exception e) {
|
||||
ErrorsCounter.methodError(this, errStr, e);
|
||||
}
|
||||
|
||||
public MethodInfo getMethodInfo() {
|
||||
return mthInfo;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class DebugInfoVisitor extends AbstractVisitor {
|
||||
processDebugInfo(mth, debugOffset);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error in debug info parser: {}", ErrorsCounter.formatErrorMsg(mth, e.getMessage()), e);
|
||||
LOG.error("Error in debug info parser: {}", ErrorsCounter.formatMsg(mth, e.getMessage()), e);
|
||||
} finally {
|
||||
mth.unloadInsnArr();
|
||||
}
|
||||
|
||||
@@ -259,8 +259,7 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
} else {
|
||||
CodeWriter code = new CodeWriter();
|
||||
List<InsnNode> instructions = block.getInstructions();
|
||||
MethodGen.addFallbackInsns(code, mth,
|
||||
instructions.toArray(new InsnNode[instructions.size()]), false);
|
||||
MethodGen.addFallbackInsns(code, mth, instructions.toArray(new InsnNode[0]), false);
|
||||
String str = escape(code.newLine().toString());
|
||||
if (str.startsWith(NL)) {
|
||||
str = str.substring(NL.length());
|
||||
|
||||
@@ -366,7 +366,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
elType = insnElementType;
|
||||
}
|
||||
if (!elType.equals(insnElementType) && !insnArrayType.equals(ArgType.OBJECT)) {
|
||||
ErrorsCounter.methodError(mth,
|
||||
ErrorsCounter.methodWarn(mth,
|
||||
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
||||
+ ", element type: " + elType + ", insn element type: " + insnElementType
|
||||
);
|
||||
|
||||
@@ -529,7 +529,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
return false;
|
||||
}
|
||||
if (removeInfo.getOuts().isEmpty()) {
|
||||
ErrorsCounter.methodError(mth, "Failed to extract finally block: empty outs");
|
||||
ErrorsCounter.methodWarn(mth, "Failed to extract finally block: empty outs");
|
||||
return false;
|
||||
}
|
||||
// safe checks finished, altering blocks tree
|
||||
|
||||
@@ -6,15 +6,19 @@ import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.codegen.InsnGen;
|
||||
import jadx.core.codegen.MethodGen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
public class CheckRegions extends AbstractVisitor {
|
||||
@@ -23,6 +27,7 @@ public class CheckRegions extends AbstractVisitor {
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.isNoCode()
|
||||
|| mth.getRegion() == null
|
||||
|| mth.getBasicBlocks().isEmpty()
|
||||
|| mth.contains(AType.JADX_ERROR)) {
|
||||
return;
|
||||
@@ -44,9 +49,10 @@ public class CheckRegions extends AbstractVisitor {
|
||||
&& !block.contains(AFlag.SKIP)
|
||||
&& !block.contains(AFlag.SYNTHETIC)
|
||||
&& !block.getInstructions().isEmpty()) {
|
||||
// TODO
|
||||
// mth.add(AFlag.INCONSISTENT_CODE);
|
||||
LOG.debug(" Duplicated block: {} in {}", block, mth);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Duplicated block: {} - {}", mth, block);
|
||||
}
|
||||
//mth.addWarn("Duplicated block: " + block);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -55,8 +61,8 @@ public class CheckRegions extends AbstractVisitor {
|
||||
if (!blocksInRegions.contains(block)
|
||||
&& !block.getInstructions().isEmpty()
|
||||
&& !block.contains(AFlag.SKIP)) {
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
LOG.debug(" Missing block: {} in {}", block, mth);
|
||||
String blockCode = getBlockInsnStr(mth, block);
|
||||
mth.addWarn("Missing block: " + block + ", code:" + CodeWriter.NL + blockCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,11 +74,27 @@ public class CheckRegions extends AbstractVisitor {
|
||||
if (region instanceof LoopRegion) {
|
||||
BlockNode loopHeader = ((LoopRegion) region).getHeader();
|
||||
if (loopHeader != null && loopHeader.getInstructions().size() != 1) {
|
||||
ErrorsCounter.methodError(mth, "Incorrect condition in loop: " + loopHeader);
|
||||
mth.addWarn("Incorrect condition in loop: " + loopHeader);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String getBlockInsnStr(MethodNode mth, BlockNode block) {
|
||||
CodeWriter code = new CodeWriter();
|
||||
code.setIndent(3);
|
||||
MethodGen mg = MethodGen.getFallbackMethodGen(mth);
|
||||
InsnGen ig = new InsnGen(mg, true);
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
try {
|
||||
ig.makeInsn(insn, code);
|
||||
} catch (CodegenException ignored) {
|
||||
}
|
||||
}
|
||||
code.newLine().addIndent();
|
||||
code.finish();
|
||||
return code.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
}
|
||||
TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
|
||||
if (prevTB != null) {
|
||||
ErrorsCounter.methodError(mth, "Failed to process nested try/catch");
|
||||
ErrorsCounter.methodWarn(mth, "Failed to process nested try/catch");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
if (region.getSubBlocks().contains(dominator)) {
|
||||
TryCatchBlock tb = tryBlocksMap.get(dominator);
|
||||
if (!wrapBlocks(region, tb, dominator)) {
|
||||
ErrorsCounter.methodError(mth, "Can't wrap try/catch for " + region);
|
||||
ErrorsCounter.methodWarn(mth, "Can't wrap try/catch for " + region);
|
||||
}
|
||||
tryBlocksMap.remove(dominator);
|
||||
return true;
|
||||
|
||||
@@ -532,7 +532,7 @@ public class RegionMaker {
|
||||
|
||||
BlockNode body = getNextBlock(block);
|
||||
if (body == null) {
|
||||
ErrorsCounter.methodError(mth, "Unexpected end of synchronized block");
|
||||
ErrorsCounter.methodWarn(mth, "Unexpected end of synchronized block");
|
||||
return null;
|
||||
}
|
||||
BlockNode exit = null;
|
||||
@@ -904,7 +904,7 @@ public class RegionMaker {
|
||||
blocks.add(handlerBlock);
|
||||
splitters.addAll(handlerBlock.getPredecessors());
|
||||
} else {
|
||||
LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler));
|
||||
LOG.debug(ErrorsCounter.formatMsg(mth, "No exception handler block: " + handler));
|
||||
}
|
||||
}
|
||||
Set<BlockNode> exits = new HashSet<>();
|
||||
@@ -912,7 +912,7 @@ public class RegionMaker {
|
||||
for (BlockNode handler : blocks) {
|
||||
List<BlockNode> s = splitter.getSuccessors();
|
||||
if (s.isEmpty()) {
|
||||
LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter));
|
||||
LOG.debug(ErrorsCounter.formatMsg(mth, "No successors for splitter: " + splitter));
|
||||
continue;
|
||||
}
|
||||
BlockNode ss = s.get(0);
|
||||
|
||||
@@ -23,6 +23,6 @@ public class CheckTypeVisitor {
|
||||
}
|
||||
|
||||
private static void error(String msg, MethodNode mth, InsnNode insn) {
|
||||
ErrorsCounter.methodError(mth, msg + ": " + insn);
|
||||
ErrorsCounter.methodWarn(mth, msg + ": " + insn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JadxWarn;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.IDexNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.JadxOverflowException;
|
||||
|
||||
@@ -22,34 +25,53 @@ public class ErrorsCounter {
|
||||
|
||||
private final Set<IAttributeNode> errorNodes = new HashSet<>();
|
||||
private int errorsCount;
|
||||
private final Set<IAttributeNode> warnNodes = new HashSet<>();
|
||||
private int warnsCount;
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorsCount;
|
||||
}
|
||||
|
||||
private synchronized void addError(IAttributeNode node, String msg, @Nullable Throwable e) {
|
||||
public int getWarnsCount() {
|
||||
return warnsCount;
|
||||
}
|
||||
|
||||
private synchronized <N extends IDexNode & IAttributeNode> String addError(N node, String error, @Nullable Throwable e) {
|
||||
errorNodes.add(node);
|
||||
errorsCount++;
|
||||
|
||||
if (e != null) {
|
||||
if (e instanceof JadxOverflowException) {
|
||||
// don't print full stack trace
|
||||
e = new JadxOverflowException(e.getMessage());
|
||||
LOG.error("{}, message: {}", msg, e.getMessage());
|
||||
} else {
|
||||
LOG.error(msg, e);
|
||||
}
|
||||
node.addAttr(new JadxErrorAttr(e));
|
||||
} else {
|
||||
node.add(AFlag.INCONSISTENT_CODE);
|
||||
String msg = formatMsg(node, error);
|
||||
if (e == null) {
|
||||
LOG.error(msg);
|
||||
} else if (e instanceof JadxOverflowException) {
|
||||
// don't print full stack trace
|
||||
e = new JadxOverflowException(e.getMessage());
|
||||
LOG.error("{}, details: {}", msg, e.getMessage());
|
||||
} else {
|
||||
LOG.error(msg, e);
|
||||
}
|
||||
|
||||
node.addAttr(AType.JADX_ERROR, new JadxError(error, e));
|
||||
node.remove(AFlag.INCONSISTENT_CODE);
|
||||
return msg;
|
||||
}
|
||||
|
||||
private synchronized <N extends IDexNode & IAttributeNode> String addWarning(N node, String warn) {
|
||||
warnNodes.add(node);
|
||||
warnsCount++;
|
||||
|
||||
node.addAttr(AType.JADX_WARN, new JadxWarn(warn));
|
||||
if (!node.contains(AType.JADX_ERROR)) {
|
||||
node.add(AFlag.INCONSISTENT_CODE);
|
||||
}
|
||||
|
||||
String msg = formatMsg(node, warn);
|
||||
LOG.warn(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static String classError(ClassNode cls, String errorMsg, Throwable e) {
|
||||
String msg = formatErrorMsg(cls, errorMsg);
|
||||
cls.dex().root().getErrorsCounter().addError(cls, msg, e);
|
||||
return msg;
|
||||
return cls.dex().root().getErrorsCounter().addError(cls, errorMsg, e);
|
||||
}
|
||||
|
||||
public static String classError(ClassNode cls, String errorMsg) {
|
||||
@@ -57,13 +79,15 @@ public class ErrorsCounter {
|
||||
}
|
||||
|
||||
public static String methodError(MethodNode mth, String errorMsg, Throwable e) {
|
||||
String msg = formatErrorMsg(mth, errorMsg);
|
||||
mth.dex().root().getErrorsCounter().addError(mth, msg, e);
|
||||
return msg;
|
||||
return mth.root().getErrorsCounter().addError(mth, errorMsg, e);
|
||||
}
|
||||
|
||||
public static String methodError(MethodNode mth, String errorMsg) {
|
||||
return methodError(mth, errorMsg, null);
|
||||
public static String methodWarn(MethodNode mth, String warnMsg) {
|
||||
return mth.root().getErrorsCounter().addWarning(mth, warnMsg);
|
||||
}
|
||||
|
||||
public static String formatMsg(IDexNode node, String msg) {
|
||||
return msg + " in " + node.typeName() + ": " + node + ", dex: " + node.dex().getDexFile().getName();
|
||||
}
|
||||
|
||||
public void printReport() {
|
||||
@@ -79,13 +103,8 @@ public class ErrorsCounter {
|
||||
LOG.error(" {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatErrorMsg(ClassNode cls, String msg) {
|
||||
return msg + " in class: " + cls + ", dex: " + cls.dex().getDexFile().getName();
|
||||
}
|
||||
|
||||
public static String formatErrorMsg(MethodNode mth, String msg) {
|
||||
return msg + " in method: " + mth + ", dex: " + mth.dex().getDexFile().getName();
|
||||
if (getWarnsCount() > 0) {
|
||||
LOG.warn("{} warnings in {} nodes", getWarnsCount(), warnNodes.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
@@ -9,6 +10,7 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
|
||||
public class Utils {
|
||||
|
||||
@@ -80,6 +82,30 @@ public class Utils {
|
||||
return sw.getBuffer().toString();
|
||||
}
|
||||
|
||||
public static void appendStackTrace(CodeWriter code, Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
return;
|
||||
}
|
||||
code.startLine();
|
||||
OutputStream w = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) {
|
||||
char c = (char) b;
|
||||
if (c == '\r') {
|
||||
// ignore
|
||||
} else if (c == '\n') {
|
||||
code.startLine();
|
||||
} else {
|
||||
code.add(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
PrintWriter pw = new PrintWriter(w, true);
|
||||
filterRecursive(throwable);
|
||||
throwable.printStackTrace(pw);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
private static void filterRecursive(Throwable th) {
|
||||
try {
|
||||
filter(th);
|
||||
|
||||
@@ -17,11 +17,11 @@ public class JadxException extends Exception {
|
||||
}
|
||||
|
||||
public JadxException(ClassNode cls, String msg, Throwable th) {
|
||||
super(ErrorsCounter.formatErrorMsg(cls, msg), th);
|
||||
super(ErrorsCounter.formatMsg(cls, msg), th);
|
||||
}
|
||||
|
||||
public JadxException(MethodNode mth, String msg, Throwable th) {
|
||||
super(ErrorsCounter.formatErrorMsg(mth, msg), th);
|
||||
super(ErrorsCounter.formatMsg(mth, msg), th);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user