diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index aef7dcd67..c03b87e93 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -43,9 +43,6 @@ import jadx.api.args.DeobfuscationMapFileMode; import jadx.api.metadata.ICodeMetadata; import jadx.api.metadata.annotations.InsnCodeOffset; import jadx.core.dex.attributes.AFlag; -import jadx.core.dex.attributes.AType; -import jadx.core.dex.attributes.IAttributeNode; -import jadx.core.dex.attributes.nodes.JadxCommentsAttr; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; @@ -63,13 +60,11 @@ import jadx.tests.api.utils.TestUtils; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.apache.commons.lang3.StringUtils.rightPad; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; @@ -289,7 +284,7 @@ public abstract class IntegrationTest extends TestUtils { } protected void runChecks(List clsList) { - clsList.forEach(this::checkCode); + clsList.forEach(cls -> checkCode(cls, allowWarnInCode)); compileClassNode(clsList); clsList.forEach(this::runAutoCheck); } @@ -347,33 +342,6 @@ public abstract class IntegrationTest extends TestUtils { root.processResources(resStorage); } - protected void checkCode(ClassNode cls) { - assertFalse(hasErrors(cls), "Inconsistent cls: " + cls); - for (MethodNode mthNode : cls.getMethods()) { - if (hasErrors(mthNode)) { - fail("Method with problems: " + mthNode - + "\n " + Utils.listToString(mthNode.getAttributesStringsList(), "\n ")); - } - } - - String code = cls.getCode().getCodeStr(); - assertThat(code, not(containsString("inconsistent"))); - assertThat(code, not(containsString("JADX ERROR"))); - } - - private boolean hasErrors(IAttributeNode node) { - if (node.contains(AFlag.INCONSISTENT_CODE) || node.contains(AType.JADX_ERROR)) { - return true; - } - if (!allowWarnInCode) { - JadxCommentsAttr commentsAttr = node.get(AType.JADX_COMMENTS); - if (commentsAttr != null) { - return commentsAttr.getComments().get(CommentsLevel.WARN) != null; - } - } - return false; - } - private void runAutoCheck(ClassNode cls) { String clsName = cls.getClassInfo().getRawName().replace('/', '.'); try { diff --git a/jadx-core/src/test/java/jadx/tests/api/utils/TestUtils.java b/jadx-core/src/test/java/jadx/tests/api/utils/TestUtils.java index 0f5dd297a..bd966a1cf 100644 --- a/jadx-core/src/test/java/jadx/tests/api/utils/TestUtils.java +++ b/jadx-core/src/test/java/jadx/tests/api/utils/TestUtils.java @@ -3,7 +3,21 @@ package jadx.tests.api.utils; import org.junit.jupiter.api.extension.ExtendWith; import jadx.NotYetImplementedExtension; +import jadx.api.CommentsLevel; import jadx.api.ICodeWriter; +import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.IAttributeNode; +import jadx.core.dex.attributes.nodes.JadxCommentsAttr; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.Utils; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; @ExtendWith(NotYetImplementedExtension.class) public class TestUtils { @@ -35,4 +49,31 @@ public class TestUtils { } return count; } + + protected static void checkCode(ClassNode cls, boolean allowWarnInCode) { + assertFalse(hasErrors(cls, allowWarnInCode), "Inconsistent cls: " + cls); + for (MethodNode mthNode : cls.getMethods()) { + if (hasErrors(mthNode, allowWarnInCode)) { + fail("Method with problems: " + mthNode + + "\n " + Utils.listToString(mthNode.getAttributesStringsList(), "\n ")); + } + } + + String code = cls.getCode().getCodeStr(); + assertThat(code, not(containsString("inconsistent"))); + assertThat(code, not(containsString("JADX ERROR"))); + } + + protected static boolean hasErrors(IAttributeNode node, boolean allowWarnInCode) { + if (node.contains(AFlag.INCONSISTENT_CODE) || node.contains(AType.JADX_ERROR)) { + return true; + } + if (!allowWarnInCode) { + JadxCommentsAttr commentsAttr = node.get(AType.JADX_COMMENTS); + if (commentsAttr != null) { + return commentsAttr.getComments().get(CommentsLevel.WARN) != null; + } + } + return false; + } } diff --git a/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java b/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java index de9fb6bb1..d45a23481 100644 --- a/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java +++ b/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java @@ -13,21 +13,24 @@ import jadx.api.ICodeWriter; import jadx.api.JadxArgs; import jadx.api.JadxDecompiler; import jadx.api.JadxInternalAccess; +import jadx.api.metadata.ICodeAnnotation; import jadx.api.metadata.ICodeNodeRef; 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.IntegrationTest; +import jadx.tests.api.utils.TestUtils; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -public abstract class BaseExternalTest extends IntegrationTest { +public abstract class BaseExternalTest extends TestUtils { private static final Logger LOG = LoggerFactory.getLogger(BaseExternalTest.class); + protected JadxDecompiler decompiler; + protected abstract String getSamplesDir(); protected JadxArgs prepare(String inputFile) { @@ -55,16 +58,16 @@ public abstract class BaseExternalTest extends IntegrationTest { } protected JadxDecompiler decompile(JadxArgs jadxArgs, @Nullable String clsPatternStr, @Nullable String mthPatternStr) { - JadxDecompiler jadx = new JadxDecompiler(jadxArgs); - jadx.load(); + decompiler = new JadxDecompiler(jadxArgs); + decompiler.load(); if (clsPatternStr == null) { - jadx.save(); + decompiler.save(); } else { - processByPatterns(jadx, clsPatternStr, mthPatternStr); + processByPatterns(decompiler, clsPatternStr, mthPatternStr); } - printErrorReport(jadx); - return jadx; + printErrorReport(decompiler); + return decompiler; } private void processByPatterns(JadxDecompiler jadx, String clsPattern, @Nullable String mthPattern) { @@ -109,7 +112,7 @@ public abstract class BaseExternalTest extends IntegrationTest { } else { LOG.info("Code: \n{}", classNode.getCode()); } - checkCode(classNode); + checkCode(classNode, false); return true; } @@ -134,7 +137,7 @@ public abstract class BaseExternalTest extends IntegrationTest { String dashLine = "======================================================================================"; for (MethodNode mth : classNode.getMethods()) { if (isMthMatch(mth, mthPattern)) { - String mthCode = cutMethodCode(codeInfo, code, mth); + String mthCode = cutMethodCode(codeInfo, mth); LOG.info("Print method: {}\n{}\n{}\n{}", mth.getMethodInfo().getShortId(), dashLine, mthCode, @@ -143,36 +146,33 @@ public abstract class BaseExternalTest extends IntegrationTest { } } - @NotNull - private String cutMethodCode(ICodeInfo codeInfo, String code, MethodNode mth) { - int defPos = mth.getDefPosition(); - int startPos = getCommentStartPos(code, defPos); - ICodeNodeRef nodeBelow = codeInfo.getCodeMetadata().getNodeBelow(defPos); - int stopPos = nodeBelow == null ? code.length() : nodeBelow.getDefPosition(); - int brackets = 0; - StringBuilder mthCode = new StringBuilder(); - for (int i = startPos; i > 0 && i < stopPos;) { - int codePoint = code.codePointAt(i); - mthCode.appendCodePoint(codePoint); - if (i >= defPos) { - // also count brackets for detect method end - if (codePoint == '{') { - brackets++; - } else if (codePoint == '}') { - brackets--; - if (brackets <= 0) { - break; - } - } - } - i += Character.charCount(codePoint); - } - return mthCode.toString(); + private String cutMethodCode(ICodeInfo codeInfo, MethodNode mth) { + int startPos = getCommentStartPos(codeInfo, mth.getDefPosition()); + int stopPos = getNextNodePos(mth, codeInfo); + return codeInfo.getCodeStr().substring(startPos, stopPos); } - protected int getCommentStartPos(String code, int pos) { + private int getNextNodePos(MethodNode mth, ICodeInfo codeInfo) { + int pos = mth.getDefPosition() + 1; + while (true) { + ICodeNodeRef nodeBelow = codeInfo.getCodeMetadata().getNodeBelow(pos); + if (nodeBelow == null) { + return codeInfo.getCodeStr().length(); + } + if (nodeBelow.getAnnType() != ICodeAnnotation.AnnType.METHOD) { + return nodeBelow.getDefPosition(); + } + MethodNode nodeMth = (MethodNode) nodeBelow; + if (nodeMth.getParentClass().equals(mth.getParentClass())) { // skip methods from anonymous classes + return getCommentStartPos(codeInfo, nodeMth.getDefPosition()); + } + pos = nodeMth.getDefPosition() + 1; + } + } + + protected int getCommentStartPos(ICodeInfo codeInfo, int pos) { String emptyLine = ICodeWriter.NL + ICodeWriter.NL; - int emptyLinePos = code.lastIndexOf(emptyLine, pos); + int emptyLinePos = codeInfo.getCodeStr().lastIndexOf(emptyLine, pos); return emptyLinePos == -1 ? pos : emptyLinePos + emptyLine.length(); }