* feat(gui): add code comments (#359) * refactor: replace instanceof search with method dispatch in RegionGen * fix: various bug fixes and improvements for code comments * fix(gui): support multiline code comments * fix: resolve code differences after class reload * fix(gui): add search for comments, allow search in active tab only * fix: correct search for inner classes * fix(gui): run full index on search dialog open
This commit is contained in:
@@ -7,13 +7,18 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
|
||||
public class JadxAssertions extends Assertions {
|
||||
|
||||
public static JadxClassNodeAssertions assertThat(ClassNode actual) {
|
||||
Assertions.assertThat(actual).isNotNull();
|
||||
return new JadxClassNodeAssertions(actual);
|
||||
public static JadxClassNodeAssertions assertThat(ClassNode cls) {
|
||||
Assertions.assertThat(cls).isNotNull();
|
||||
return new JadxClassNodeAssertions(cls);
|
||||
}
|
||||
|
||||
public static JadxCodeAssertions assertThat(ICodeInfo actual) {
|
||||
Assertions.assertThat(actual).isNotNull();
|
||||
return new JadxCodeAssertions(actual.getCodeStr());
|
||||
public static JadxCodeInfoAssertions assertThat(ICodeInfo codeInfo) {
|
||||
Assertions.assertThat(codeInfo).isNotNull();
|
||||
return new JadxCodeInfoAssertions(codeInfo);
|
||||
}
|
||||
|
||||
public static JadxCodeAssertions assertThat(String code) {
|
||||
Assertions.assertThat(code).isNotNull();
|
||||
return new JadxCodeAssertions(code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.tests.api.utils.assertj;
|
||||
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -13,10 +14,17 @@ public class JadxClassNodeAssertions extends AbstractObjectAssert<JadxClassNodeA
|
||||
super(cls, JadxClassNodeAssertions.class);
|
||||
}
|
||||
|
||||
public JadxCodeInfoAssertions decompile() {
|
||||
isNotNull();
|
||||
ICodeInfo codeInfo = actual.getCode();
|
||||
Assertions.assertThat(codeInfo).isNotNull();
|
||||
return new JadxCodeInfoAssertions(codeInfo);
|
||||
}
|
||||
|
||||
public JadxCodeAssertions code() {
|
||||
isNotNull();
|
||||
ICodeInfo code = actual.getCode();
|
||||
assertThat(code).isNotNull();
|
||||
Assertions.assertThat(code).isNotNull();
|
||||
String codeStr = code.getCodeStr();
|
||||
assertThat(codeStr).isNotBlank();
|
||||
return new JadxCodeAssertions(codeStr);
|
||||
@@ -25,9 +33,9 @@ public class JadxClassNodeAssertions extends AbstractObjectAssert<JadxClassNodeA
|
||||
public JadxCodeAssertions reloadCode(IntegrationTest testInstance) {
|
||||
isNotNull();
|
||||
ICodeInfo code = actual.reloadCode();
|
||||
assertThat(code).isNotNull();
|
||||
Assertions.assertThat(code).isNotNull();
|
||||
String codeStr = code.getCodeStr();
|
||||
assertThat(codeStr).isNotBlank();
|
||||
Assertions.assertThat(codeStr).isNotBlank();
|
||||
|
||||
JadxCodeAssertions codeAssertions = new JadxCodeAssertions(codeStr);
|
||||
codeAssertions.print();
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package jadx.tests.api.utils.assertj;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.data.annotations.ICodeRawOffset;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class JadxCodeInfoAssertions extends AbstractObjectAssert<JadxCodeInfoAssertions, ICodeInfo> {
|
||||
public JadxCodeInfoAssertions(ICodeInfo cls) {
|
||||
super(cls, JadxCodeInfoAssertions.class);
|
||||
}
|
||||
|
||||
public JadxCodeAssertions code() {
|
||||
isNotNull();
|
||||
String codeStr = actual.getCodeStr();
|
||||
assertThat(codeStr).isNotBlank();
|
||||
return new JadxCodeAssertions(codeStr);
|
||||
}
|
||||
|
||||
public JadxCodeInfoAssertions checkCodeOffsets() {
|
||||
long dupOffsetCount = actual.getAnnotations().values().stream()
|
||||
.filter(o -> o instanceof ICodeRawOffset)
|
||||
.collect(Collectors.groupingBy(o -> ((ICodeRawOffset) o).getOffset(), Collectors.toList()))
|
||||
.values().stream()
|
||||
.filter(list -> list.size() > 1)
|
||||
.count();
|
||||
assertThat(dupOffsetCount)
|
||||
.describedAs("Found duplicated code offsets")
|
||||
.isEqualTo(0);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,8 @@ public class TestClassReGen extends IntegrationTest {
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
assertThat(cls.getCode())
|
||||
assertThat(cls)
|
||||
.code()
|
||||
.containsOnlyOnce("private int intField = 5;")
|
||||
.containsOnlyOnce("public static class A {")
|
||||
.containsOnlyOnce("public int test() {");
|
||||
@@ -32,8 +33,8 @@ public class TestClassReGen extends IntegrationTest {
|
||||
cls.searchMethodByShortName("test").getMethodInfo().setAlias("testRenamed");
|
||||
cls.searchFieldByName("intField").getFieldInfo().setAlias("intFieldRenamed");
|
||||
|
||||
assertThat(cls.reloadCode())
|
||||
.print()
|
||||
assertThat(cls)
|
||||
.reloadCode(this)
|
||||
.containsOnlyOnce("private int intFieldRenamed = 5;")
|
||||
.containsOnlyOnce("public static class ARenamed {")
|
||||
.containsOnlyOnce("public int testRenamed() {");
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeComment;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxNodeRef;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestCodeComments extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
private int intField = 5;
|
||||
|
||||
public static class A {
|
||||
}
|
||||
|
||||
public int test() {
|
||||
System.out.println("Hello");
|
||||
System.out.println("comment");
|
||||
return intField;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String baseClsId = TestCls.class.getName();
|
||||
ICodeComment clsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId), "class comment");
|
||||
ICodeComment innerClsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId + ".A"), "inner class comment");
|
||||
ICodeComment fldComment = new JadxCodeComment(new JadxNodeRef(RefType.FIELD, baseClsId, "intField:I"), "field comment");
|
||||
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test()I");
|
||||
ICodeComment mthComment = new JadxCodeComment(mthRef, "method comment");
|
||||
ICodeComment insnComment = new JadxCodeComment(mthRef, "insn comment", 11);
|
||||
|
||||
JadxCodeData codeData = new JadxCodeData();
|
||||
getArgs().setCodeData(codeData);
|
||||
codeData.setComments(Arrays.asList(clsComment, innerClsComment, fldComment, mthComment, insnComment));
|
||||
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
assertThat(cls)
|
||||
.decompile()
|
||||
.checkCodeOffsets()
|
||||
.code()
|
||||
.containsOne("// class comment")
|
||||
.containsOne("// inner class comment")
|
||||
.containsOne("// field comment")
|
||||
.containsOne("// method comment")
|
||||
.containsOne("System.out.println(\"comment\"); // insn comment");
|
||||
|
||||
String code = cls.getCode().getCodeStr();
|
||||
assertThat(cls)
|
||||
.reloadCode(this)
|
||||
.isEqualTo(code);
|
||||
|
||||
ICodeComment updInsnComment = new JadxCodeComment(mthRef, "updated insn comment", 11);
|
||||
codeData.setComments(Collections.singletonList(updInsnComment));
|
||||
assertThat(cls)
|
||||
.reloadCode(this)
|
||||
.containsOne("System.out.println(\"comment\"); // updated insn comment")
|
||||
.doesNotContain("class comment")
|
||||
.containsOne(" comment");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeComment;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxNodeRef;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestCodeComments2 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public int test(boolean z) {
|
||||
if (z) {
|
||||
System.out.println("z");
|
||||
return 1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String baseClsId = TestCls.class.getName();
|
||||
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I");
|
||||
ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", 10);
|
||||
ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", 11);
|
||||
|
||||
JadxCodeData codeData = new JadxCodeData();
|
||||
codeData.setComments(Arrays.asList(insnComment, insnComment2));
|
||||
getArgs().setCodeData(codeData);
|
||||
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.decompile()
|
||||
.checkCodeOffsets()
|
||||
.code()
|
||||
.containsOne("// " + insnComment.getComment())
|
||||
.containsOne("// " + insnComment2.getComment());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeComment;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxNodeRef;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestCodeComments2a extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
private int f;
|
||||
|
||||
public int test(boolean z) {
|
||||
if (z) {
|
||||
System.out.println("z");
|
||||
return new Random().nextInt();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String baseClsId = TestCls.class.getName();
|
||||
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I");
|
||||
ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", 18);
|
||||
ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", 19);
|
||||
|
||||
JadxCodeData codeData = new JadxCodeData();
|
||||
codeData.setComments(Arrays.asList(insnComment, insnComment2));
|
||||
getArgs().setCodeData(codeData);
|
||||
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.decompile()
|
||||
.checkCodeOffsets()
|
||||
.code()
|
||||
.containsOne("// " + insnComment.getComment())
|
||||
.containsOne("// " + insnComment2.getComment());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeComment;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxNodeRef;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestCodeCommentsMultiline extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public int test(boolean z) {
|
||||
if (z) {
|
||||
System.out.println("z");
|
||||
return 1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String baseClsId = TestCls.class.getName();
|
||||
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I");
|
||||
ICodeComment insnComment = new JadxCodeComment(mthRef, "multi\nline\ncomment", 11);
|
||||
|
||||
JadxCodeData codeData = new JadxCodeData();
|
||||
codeData.setComments(Collections.singletonList(insnComment));
|
||||
getArgs().setCodeData(codeData);
|
||||
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("// multi")
|
||||
.containsOne("// line")
|
||||
.containsOne("// comment");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package jadx.tests.integration.others;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.data.ICodeComment;
|
||||
import jadx.api.data.IJavaNodeRef.RefType;
|
||||
import jadx.api.data.impl.JadxCodeComment;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.data.impl.JadxNodeRef;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestCodeCommentsOverride extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public interface I {
|
||||
void mth();
|
||||
}
|
||||
|
||||
public static class A implements I {
|
||||
@Override
|
||||
public void mth() {
|
||||
System.out.println("mth");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String baseClsId = TestCls.class.getName();
|
||||
JadxNodeRef iMthRef = new JadxNodeRef(RefType.METHOD, baseClsId + ".I", "mth()V");
|
||||
ICodeComment iMthComment = new JadxCodeComment(iMthRef, "interface mth comment");
|
||||
|
||||
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId + ".A", "mth()V");
|
||||
ICodeComment mthComment = new JadxCodeComment(mthRef, "mth comment");
|
||||
|
||||
JadxCodeData codeData = new JadxCodeData();
|
||||
codeData.setComments(Arrays.asList(iMthComment, mthComment));
|
||||
getArgs().setCodeData(codeData);
|
||||
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
assertThat(cls)
|
||||
.decompile()
|
||||
.checkCodeOffsets()
|
||||
.code()
|
||||
.containsOne("@Override")
|
||||
.containsOne("// " + iMthComment.getComment())
|
||||
.containsOne("// " + mthComment.getComment());
|
||||
|
||||
assertThat(cls)
|
||||
.reloadCode(this)
|
||||
.containsOne("@Override")
|
||||
.containsOne("// " + iMthComment.getComment())
|
||||
.containsOne("// " + mthComment.getComment());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user