diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index 56e1b096c..1106f5d63 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -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; } } diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index 1b242b6ba..b83ffad52 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 3a53a69eb..66e404b3b 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -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 errors = mth.getAll(AType.JADX_ERROR); + List 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()) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index afd017c2a..2147ff2c3 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -191,6 +191,11 @@ public class CodeWriter { return indent; } + public void setIndent(int indent) { + this.indent = indent; + updateIndent(); + } + public int getLine() { return line; } diff --git a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java index db3527f28..ec0f30d9e 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java @@ -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)); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index f68d4679f..d18f5bfc0 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 174751ef4..bf1e15e93 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -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); } - } diff --git a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java index 81363f4cf..f6d61e3e2 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java @@ -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) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index f6ad43c50..72f241525 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -153,7 +153,7 @@ public class RegionGen extends InsnGen { if (header != null) { List 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); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java index ca024e0ac..d63c79726 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java @@ -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 { public static final AType> LOOP = new AType<>(); public static final AType> EDGE_INSN = new AType<>(); + public static final AType> JADX_ERROR = new AType<>(); + public static final AType> JADX_WARN = new AType<>(); + public static final AType EXC_HANDLER = new AType<>(); public static final AType CATCH_BLOCK = new AType<>(); public static final AType SPLITTER_BLOCK = new AType<>(); public static final AType FORCE_RETURN = new AType<>(); public static final AType FIELD_INIT = new AType<>(); public static final AType FIELD_REPLACE = new AType<>(); - public static final AType JADX_ERROR = new AType<>(); public static final AType METHOD_INLINE = new AType<>(); public static final AType ENUM_CLASS = new AType<>(); public static final AType ENUM_MAP = new AType<>(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java index 3c49c22cd..00e37a572 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrList.java @@ -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 implements IAttribute { private final AType> type; - private final List list = new LinkedList<>(); + private final List list = new ArrayList<>(); public AttrList(AType> type) { this.type = type; diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxErrorAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxError.java similarity index 59% rename from jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxErrorAttr.java rename to jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxError.java index 71745240b..12e0766a7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxErrorAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxError.java @@ -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 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(); } - } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxWarn.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxWarn.java new file mode 100644 index 000000000..9a6e258c5 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxWarn.java @@ -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; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index abe3121f2..cbc2da87c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -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); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java index 73acca6da..1ab674ba7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java @@ -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 dominatesOn = Collections.emptyList(); + private List 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 toRemove = new LinkedList<>(); + List 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); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 9fb494a25..fa6d2ab8e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -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(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java index 61156928f..d2116262c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java @@ -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; } - } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java index 4d6bb337f..762525db0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java @@ -2,6 +2,8 @@ package jadx.core.dex.nodes; public interface IDexNode { + String typeName(); + DexNode dex(); RootNode root(); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 7569444fb..50affbac3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -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; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DebugInfoVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DebugInfoVisitor.java index 622508cf3..bb8cdfe45 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DebugInfoVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DebugInfoVisitor.java @@ -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(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 066fef748..243f123be 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -259,8 +259,7 @@ public class DotGraphVisitor extends AbstractVisitor { } else { CodeWriter code = new CodeWriter(); List 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()); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 6d46d7b88..740ab5092 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -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 ); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java index affa9da84..0bf879e04 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java @@ -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 diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java index e06ba237c..3dbcd98a5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java @@ -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(); + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java index 375f4525d..33d544cc3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index de23207f9..8334da4ea 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -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 exits = new HashSet<>(); @@ -912,7 +912,7 @@ public class RegionMaker { for (BlockNode handler : blocks) { List 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); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java index 74735d264..5e6c96667 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java @@ -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); } } diff --git a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java index baca48898..7a40c51ac 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java +++ b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java @@ -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 errorNodes = new HashSet<>(); private int errorsCount; + private final Set 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 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 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()); + } } } diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 530eabe88..1c979d0b9 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -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); diff --git a/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxException.java b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxException.java index 69c7b7b0d..96afa3ae5 100644 --- a/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxException.java +++ b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxException.java @@ -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); } }