feat: input plugin for java bytecode

This commit is contained in:
Skylot
2021-08-01 17:50:39 +01:00
parent 2d9bcdb87a
commit 1efdcd7b10
242 changed files with 5988 additions and 1174 deletions
@@ -1,13 +1,11 @@
package jadx.tests.api;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -18,18 +16,21 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.jar.JarOutputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.CodePosition;
import jadx.api.ICodeInfo;
import jadx.api.ICodeWriter;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess;
import jadx.api.data.annotations.InsnCodeOffset;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrList;
@@ -47,7 +48,6 @@ import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.compiler.StaticCompiler;
import jadx.tests.api.utils.TestUtils;
import static jadx.core.utils.files.FileUtils.addFileToJar;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.apache.commons.lang3.StringUtils.rightPad;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -63,11 +63,18 @@ import static org.junit.jupiter.api.Assertions.fail;
public abstract class IntegrationTest extends TestUtils {
private static final Logger LOG = LoggerFactory.getLogger(IntegrationTest.class);
private static final String TEST_DIRECTORY = "src/test/java";
private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;
private static final String OUT_DIR = "test-out-tmp";
private static final String DEFAULT_INPUT_PLUGIN = "dx";
/**
* Set 'TEST_INPUT_PLUGIN' env variable to use 'java' or 'dx' input in tests
*/
private static final boolean USE_JAVA_INPUT = Utils.getOrElse(System.getenv("TEST_INPUT_PLUGIN"), DEFAULT_INPUT_PLUGIN).equals("java");
/**
* Run auto check method if defined:
*
@@ -80,26 +87,20 @@ public abstract class IntegrationTest extends TestUtils {
protected JadxArgs args;
protected boolean deleteTmpFiles;
protected boolean withDebugInfo;
protected boolean unloadCls;
protected boolean compile;
protected boolean useEclipseCompiler;
protected Map<Integer, String> resMap = Collections.emptyMap();
private boolean allowWarnInCode;
private boolean printLineNumbers;
private boolean printSmali;
private boolean printOffsets;
private boolean printDisassemble;
private Boolean useJavaInput = null;
private DynamicCompiler dynamicCompiler;
static {
// needed for post decompile check
AType.SKIP_ON_UNLOAD.addAll(Arrays.asList(
AType.JADX_ERROR,
AType.JADX_WARN,
AType.COMMENTS));
// enable debug checks
DebugChecks.checksEnabled = true;
}
@@ -108,8 +109,6 @@ public abstract class IntegrationTest extends TestUtils {
@BeforeEach
public void init() {
this.deleteTmpFiles = true;
this.unloadCls = true;
this.withDebugInfo = true;
this.compile = true;
this.useEclipseCompiler = false;
@@ -141,8 +140,9 @@ public abstract class IntegrationTest extends TestUtils {
public ClassNode getClassNode(Class<?> clazz) {
try {
File jar = getJarForClass(clazz);
return getClassNodeFromFiles(Collections.singletonList(jar), clazz.getName());
List<File> files = compileClass(clazz);
assertThat("File list is empty", files, not(empty()));
return getClassNodeFromFiles(files, clazz.getName());
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
@@ -181,6 +181,13 @@ public abstract class IntegrationTest extends TestUtils {
protected JadxDecompiler loadFiles(List<File> inputFiles) {
args.setInputFiles(inputFiles);
JadxDecompiler d = new JadxDecompiler(args);
if (isJavaInput()) {
d.getPluginManager().unload("java-convert");
LOG.info("Using java input");
} else {
d.getPluginManager().unload("java-input");
LOG.info("Using dex input");
}
try {
d.load();
} catch (Exception e) {
@@ -199,25 +206,24 @@ public abstract class IntegrationTest extends TestUtils {
}
protected void decompileAndCheck(List<ClassNode> clsList) {
if (!unloadCls) {
clsList.forEach(cls -> cls.add(AFlag.DONT_UNLOAD_CLASS));
}
clsList.forEach(cls -> cls.add(AFlag.DONT_UNLOAD_CLASS)); // keep error and warning attributes
clsList.forEach(ClassNode::decompile);
for (ClassNode cls : clsList) {
System.out.println("-----------------------------------------------------------");
ICodeInfo code = cls.getCode();
if (printLineNumbers) {
printCodeWithLineNumbers(cls.getCode());
printCodeWithLineNumbers(code);
} else if (printOffsets) {
printCodeWithOffsets(code);
} else {
System.out.println(cls.getCode());
System.out.println(code);
}
}
System.out.println("-----------------------------------------------------------");
if (printSmali) {
if (printDisassemble) {
clsList.forEach(this::printSmali);
}
runChecks(clsList);
}
@@ -233,7 +239,7 @@ public abstract class IntegrationTest extends TestUtils {
private void printSmali(ClassNode cls) {
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
System.out.println(cls.getSmali());
System.out.println(cls.getDisassembledCode());
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
@@ -253,6 +259,23 @@ public abstract class IntegrationTest extends TestUtils {
}
}
private void printCodeWithOffsets(ICodeInfo code) {
String codeStr = code.getCodeStr();
Map<CodePosition, Object> annotations = code.getAnnotations();
String[] lines = codeStr.split(ICodeWriter.NL);
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
int curLine = i + 1;
Object ann = annotations.get(new CodePosition(curLine, 0));
String offsetStr = "";
if (ann instanceof InsnCodeOffset) {
int offset = ((InsnCodeOffset) ann).getOffset();
offsetStr = "/* " + leftPad(String.valueOf(offset), 5) + " */";
}
System.out.println(rightPad(offsetStr, 12) + line);
}
}
private void insertResources(RootNode root) {
if (resMap.isEmpty()) {
return;
@@ -275,7 +298,10 @@ public abstract class IntegrationTest extends TestUtils {
+ "\n " + Utils.listToString(mthNode.getAttributesStringsList(), "\n "));
}
}
assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
String code = cls.getCode().getCodeStr();
assertThat(code, not(containsString("inconsistent")));
assertThat(code, not(containsString("JADX ERROR")));
}
private boolean hasErrors(IAttributeNode node) {
@@ -407,36 +433,6 @@ public abstract class IntegrationTest extends TestUtils {
return dynamicCompiler.invoke(cls, methodName, types, args);
}
private File getJarForClass(Class<?> cls) throws IOException {
List<File> files = compileClass(cls);
assertThat("File list is empty", files, not(empty()));
String path = cls.getPackage().getName().replace('.', '/');
File temp = createTempFile(".jar");
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp))) {
for (File file : files) {
addFileToJar(jo, file, path + '/' + file.getName());
}
}
return temp;
}
protected File createTempFile(String suffix) {
try {
Path temp;
if (deleteTmpFiles) {
temp = FileUtils.createTempFile(suffix);
} else {
// don't delete on exit
temp = FileUtils.createTempFileNoDelete(suffix);
System.out.println("Temporary file saved: " + temp.toAbsolutePath());
}
return temp.toFile();
} catch (Exception e) {
throw new AssertionError(e.getMessage());
}
}
private List<File> compileClass(Class<?> cls) throws IOException {
String clsFullName = cls.getName();
String rootClsName;
@@ -499,10 +495,6 @@ public abstract class IntegrationTest extends TestUtils {
this.compile = false;
}
protected void dontUnloadClass() {
this.unloadCls = false;
}
protected void enableDeobfuscation() {
args.setDeobfuscationOn(true);
args.setDeobfuscationForceSave(true);
@@ -518,6 +510,22 @@ public abstract class IntegrationTest extends TestUtils {
printLineNumbers = true;
}
protected void printOffsets() {
printOffsets = true;
}
protected void useJavaInput() {
this.useJavaInput = true;
}
protected void useDexInput() {
this.useJavaInput = false;
}
protected boolean isJavaInput() {
return Utils.getOrElse(useJavaInput, USE_JAVA_INPUT);
}
// Use only for debug purpose
@Deprecated
protected void outputCFG() {
@@ -527,8 +535,8 @@ public abstract class IntegrationTest extends TestUtils {
// Use only for debug purpose
@Deprecated
protected void printSmali() {
this.printSmali = true;
protected void printDisassemble() {
this.printDisassemble = true;
}
// Use only for debug purpose
@@ -536,10 +544,4 @@ public abstract class IntegrationTest extends TestUtils {
protected void outputRawCFG() {
this.args.setRawCFGOutput(true);
}
// Use only for debug purpose
@Deprecated
protected void notDeleteTmpJar() {
this.deleteTmpFiles = false;
}
}
@@ -7,6 +7,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import jadx.api.JadxInternalAccess;
import jadx.core.dex.nodes.ClassNode;
@@ -21,6 +22,12 @@ public abstract class SmaliTest extends IntegrationTest {
private static final String SMALI_TESTS_DIR = "src/test/smali";
private static final String SMALI_TESTS_EXT = ".smali";
@BeforeEach
public void init() {
super.init();
this.useDexInput();
}
protected ClassNode getClassNodeFromSmali(String file, String clsName) {
File smaliFile = getSmaliFile(file);
return getClassNodeFromFiles(Collections.singletonList(smaliFile), clsName);
@@ -1,5 +1,7 @@
package jadx.tests.api.utils.assertj;
import java.util.Arrays;
import org.assertj.core.api.AbstractStringAssert;
import jadx.api.ICodeWriter;
@@ -72,4 +74,15 @@ public class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions>
System.out.println("-----------------------------------------------------------");
return this;
}
public JadxCodeAssertions containsOneOf(String... substringArr) {
int matches = 0;
for (String substring : substringArr) {
matches += TestUtils.count(actual, substring);
}
if (matches != 1) {
failWithMessage("Expected a only one match from <%s> but was <%d>", Arrays.toString(substringArr), matches);
}
return this;
}
}
@@ -3,9 +3,9 @@ package jadx.tests.functional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttributeStorage;
import jadx.core.dex.attributes.IAttribute;
import static jadx.core.dex.attributes.AFlag.SYNTHETIC;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -35,9 +35,9 @@ public class AttributeStorageTest {
public static final AType<TestAttr> TEST = new AType<>();
public static class TestAttr implements IAttribute {
public static class TestAttr implements IJadxAttribute {
@Override
public AType<TestAttr> getType() {
public AType<TestAttr> getAttrType() {
return TEST;
}
}
@@ -6,8 +6,8 @@ import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.data.attributes.JadxAttrType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.tests.api.IntegrationTest;
@@ -56,8 +56,8 @@ public class TestRFieldRestore extends IntegrationTest {
// check 'Button' field
FieldNode buttonField = idCls.searchFieldByName("Button");
assertThat(buttonField, notNullValue());
FieldInitAttr fieldInitAttr = buttonField.get(AType.FIELD_INIT);
Integer buttonValue = (Integer) fieldInitAttr.getEncodedValue().getValue();
EncodedValue constVal = buttonField.get(JadxAttrType.CONSTANT_VALUE);
Integer buttonValue = (Integer) constVal.getValue();
assertThat(buttonValue, is(buttonConstValue));
}
}
@@ -100,6 +100,7 @@ public class TestAnnotationsMix extends IntegrationTest {
@Test
public void test() {
// useDexInput();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
@@ -13,6 +13,8 @@ public class TestArith extends IntegrationTest {
public static class TestCls {
public static final int F = 7;
public int test(int a) {
a += 2;
use(a);
@@ -25,13 +25,15 @@ public class TestArith4 extends IntegrationTest {
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("int k = b & 7;")
.containsOne("return (1 - k) & (k + 1);");
.containsOne("& 255")
.containsOneOf("return (1 - k) & (1 + k);", "return (1 - k) & (k + 1);");
}
@Test
public void testNoDebug() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code();
.code()
.containsOne("& 255");
}
}
@@ -2,11 +2,9 @@ package jadx.tests.integration.arrays;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.MatcherAssert.assertThat;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestArrayFillConstReplace extends IntegrationTest {
@@ -20,9 +18,9 @@ public class TestArrayFillConstReplace extends IntegrationTest {
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("return new int[]{127, 129, CONST_INT};"));
assertThat(getClassNode(TestCls.class))
.code()
.containsOne(" int CONST_INT = 65535;")
.containsOne("return new int[]{127, 129, CONST_INT};");
}
}
@@ -10,6 +10,7 @@ import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@SuppressWarnings("IfCanBeSwitch")
public class TestElseIf extends IntegrationTest {
public static class TestCls {
@@ -25,6 +26,7 @@ public class TestElseIf extends IntegrationTest {
r = 4;
} else {
r = -1;
System.out.println();
}
r = r * 10;
return Math.abs(r);
@@ -2,13 +2,9 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestTernaryInIf extends IntegrationTest {
@@ -24,12 +20,14 @@ public class TestTernaryInIf extends IntegrationTest {
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("return a ? b : c;"));
assertThat(code, containsOne("return (a ? b : c) ? 1 : 2;"));
assertThat(code, not(containsString("if")));
assertThat(code, not(containsString("else")));
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("if")
.doesNotContain("else")
.containsOne("return a ? b : c;")
.containsOneOf(
"return (a ? b : c) ? 1 : 2;",
"return (a ? !b : !c) ? 2 : 1;" // TODO: simplify this
);
}
}
@@ -1,7 +1,6 @@
package jadx.tests.integration.debuginfo;
import java.lang.ref.WeakReference;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -15,7 +14,7 @@ public class TestLineNumbers2 extends IntegrationTest {
public static class TestCls {
private WeakReference<TestCls> f;
// keep at line 18
// keep constructor at line 18
public TestCls(TestCls s) {
}
@@ -38,8 +37,12 @@ public class TestLineNumbers2 extends IntegrationTest {
printLineNumbers();
ClassNode cls = getClassNode(TestCls.class);
Map<Integer, Integer> lineMapping = cls.getCode().getLineMapping();
assertEquals("{6=17, 9=18, 12=22, 13=23, 14=24, 15=28, 17=25, 18=26, 19=28, 22=31, 23=32}",
lineMapping.toString());
String linesMapStr = cls.getCode().getLineMapping().toString();
if (isJavaInput()) {
assertEquals("{6=16, 9=17, 12=21, 13=22, 14=23, 16=25, 18=27, 21=30}", linesMapStr);
} else {
// TODO: invert condition to match source lines
assertEquals("{6=16, 9=17, 12=21, 13=22, 14=23, 15=27, 17=24, 18=25, 19=27, 22=30, 23=31}", linesMapStr);
}
}
}
@@ -2,7 +2,6 @@ package jadx.tests.integration.debuginfo;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.api.ICodeInfo;
import jadx.api.ICodeWriter;
import jadx.core.dex.attributes.nodes.LineAttrNode;
@@ -51,28 +50,24 @@ public class TestReturnSourceLine extends IntegrationTest {
@Test
public void test() {
printLineNumbers();
ClassNode cls = getClassNode(TestCls.class);
ICodeInfo codeInfo = cls.getCode();
String code = codeInfo.toString();
String[] lines = code.split(ICodeWriter.NL);
String[] lines = codeInfo.getCodeStr().split(ICodeWriter.NL);
MethodNode test1 = cls.searchMethodByShortId("test1(Z)I");
checkLine(lines, codeInfo, test1, 3, "return 1;");
MethodNode test2 = cls.searchMethodByShortId("test2(I)I");
checkLine(lines, codeInfo, test2, 3, "return v - 1;");
}
@Test
@NotYetImplemented
public void test2() {
ClassNode cls = getClassNode(TestCls.class);
ICodeInfo codeInfo = cls.getCode();
String code = codeInfo.toString();
String[] lines = code.split(ICodeWriter.NL);
checkLine(lines, codeInfo, test2, 6, "return v + 1;");
MethodNode test3 = cls.searchMethodByShortId("test3(I)I");
checkLine(lines, codeInfo, test3, 3, "return v;");
if (isJavaInput()) { // dx lost line number for this return
checkLine(lines, codeInfo, test3, 3, "return v;");
}
checkLine(lines, codeInfo, test3, 6, "return v + 1;");
}
private static void checkLine(String[] lines, ICodeInfo cw, LineAttrNode node, int offset, String str) {
@@ -24,6 +24,7 @@ public class TestFallbackMode extends IntegrationTest {
@Test
public void test() {
useDexInput();
setFallback();
disableCompilation();
@@ -0,0 +1,38 @@
package jadx.tests.integration.invoke;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestInvokeWithWideVars extends IntegrationTest {
@SuppressWarnings("SameParameterValue")
public static class TestCls {
public long test1() {
return call(1, 2);
}
public long test2() {
return rangeCall(1, 2, 3.0d, (byte) 4);
}
private long call(int a, long b) {
return 0L;
}
private long rangeCall(long a, int b, double c, byte d) {
return 0L;
}
}
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("return call(1, 2);")
.containsOne("return rangeCall(1, 2, 3.0d, (byte) 4);");
}
}
@@ -33,6 +33,7 @@ public class TestLambdaConstructor extends IntegrationTest {
setFallback();
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("RuntimeException::new");
.containsOne("r0 = java.lang.RuntimeException::new")
.containsOne("return r0");
}
}
@@ -29,7 +29,7 @@ public class TestArrayForEach extends IntegrationTest {
assertThat(code, containsLines(2,
"int sum = 0;",
"for (int n : a) {",
indent(1) + "sum += n;",
indent() + "sum += n;",
"}",
"return sum;"));
}
@@ -32,13 +32,15 @@ public class TestCodeComments extends IntegrationTest {
@Test
public void test() {
int insnOffset = isJavaInput() ? 13 : 11;
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);
ICodeComment insnComment = new JadxCodeComment(mthRef, "insn comment", insnOffset);
JadxCodeData codeData = new JadxCodeData();
getArgs().setCodeData(codeData);
@@ -60,7 +62,7 @@ public class TestCodeComments extends IntegrationTest {
.reloadCode(this)
.isEqualTo(code);
ICodeComment updInsnComment = new JadxCodeComment(mthRef, "updated insn comment", 11);
ICodeComment updInsnComment = new JadxCodeComment(mthRef, "updated insn comment", insnOffset);
codeData.setComments(Collections.singletonList(updInsnComment));
assertThat(cls)
.reloadCode(this)
@@ -27,10 +27,12 @@ public class TestCodeComments2 extends IntegrationTest {
@Test
public void test() {
printOffsets();
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);
ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", isJavaInput() ? 13 : 10);
ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", isJavaInput() ? 15 : 11);
JadxCodeData codeData = new JadxCodeData();
codeData.setComments(Arrays.asList(insnComment, insnComment2));
@@ -40,7 +42,7 @@ public class TestCodeComments2 extends IntegrationTest {
.decompile()
.checkCodeOffsets()
.code()
.containsOne("// " + insnComment.getComment())
.containsOne("// " + insnComment2.getComment());
.containsOne("return 1; // " + insnComment.getComment())
.containsOne("return 3; // " + insnComment2.getComment());
}
}
@@ -30,10 +30,12 @@ public class TestCodeComments2a extends IntegrationTest {
@Test
public void test() {
printOffsets();
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);
ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", isJavaInput() ? 22 : 18);
ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", isJavaInput() ? 27 : 19);
JadxCodeData codeData = new JadxCodeData();
codeData.setComments(Arrays.asList(insnComment, insnComment2));
@@ -27,9 +27,11 @@ public class TestCodeCommentsMultiline extends IntegrationTest {
@Test
public void test() {
printOffsets();
String baseClsId = TestCls.class.getName();
JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I");
ICodeComment insnComment = new JadxCodeComment(mthRef, "multi\nline\ncomment", 11);
ICodeComment insnComment = new JadxCodeComment(mthRef, "multi\nline\ncomment", isJavaInput() ? 15 : 11);
JadxCodeData codeData = new JadxCodeData();
codeData.setComments(Collections.singletonList(insnComment));
@@ -32,7 +32,6 @@ public class TestDuplicateCast extends IntegrationTest {
@Test
public void test() {
dontUnloadClass();
ClassNode cls = getClassNode(TestCls.class);
MethodNode mth = getMethod(cls, "method");
@@ -6,13 +6,9 @@ import java.util.Random;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestFieldInit extends IntegrationTest {
@@ -35,15 +31,16 @@ public class TestFieldInit extends IntegrationTest {
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("List<String> s = new ArrayList"));
assertThat(code, containsOne("A a = new A();"));
assertThat(code, containsOne("int i = (Random.class.getSimpleName().length() + 1);"));
assertThat(code, containsOne("int n = 0;"));
assertThat(code, not(containsString("static {")));
assertThat(code, containsOne("this.n = z;"));
assertThat(code, containsOne("this.n = 0;"));
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("List<String> s = new ArrayList")
.containsOne("A a = new A();")
.containsOneOf(
"int i = (Random.class.getSimpleName().length() + 1);",
"int i = (1 + Random.class.getSimpleName().length());")
.containsOne("int n = 0;")
.doesNotContain("static {")
.containsOne("this.n = z;")
.containsOne("this.n = 0;");
}
}
@@ -34,6 +34,7 @@ public class TestFieldInit2 extends IntegrationTest {
@Test
public void test() {
printDisassemble();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
@@ -2,35 +2,31 @@ package jadx.tests.integration.others;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestFloatValue extends IntegrationTest {
public static class TestCls {
public float[] method() {
public float[] test() {
float[] fa = { 0.55f };
fa[0] /= 2;
return fa;
}
public void check() {
assertEquals(0.275f, method()[0], 0.0001f);
assertEquals(0.275f, test()[0], 0.0001f);
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, not(containsString("1073741824")));
assertThat(code, containsString("0.55f"));
assertThat(code, containsString("fa[0] = fa[0] / 2.0f;"));
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("1073741824")
.containsOne("0.55f")
.containsOne("fa[0] = fa[0] / 2.0f;");
}
}
@@ -0,0 +1,36 @@
package jadx.tests.integration.others;
import org.junit.jupiter.api.Test;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestJavaDupInsn extends IntegrationTest {
public static class TestCls {
private MethodNode mth;
private BlockNode block;
private SSAVar[] vars;
private int[] versions;
public SSAVar test(RegisterArg regArg) {
int regNum = regArg.getRegNum();
int version = versions[regNum]++;
SSAVar ssaVar = mth.makeNewSVar(regNum, version, regArg);
vars[regNum] = ssaVar;
return ssaVar;
}
}
@Test
public void test() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code();
}
}
@@ -4,7 +4,6 @@ import java.util.List;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
@@ -42,7 +41,6 @@ public class TestOverridePackagePrivateMethod extends SmaliTest {
*/
// @formatter:on
@NotYetImplemented("Don't change access modifiers if not needed")
@Test
public void test() {
commonChecks();
@@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestSwitch extends IntegrationTest {
public static class TestCls {
public String escape(String str) {
public String test(String str) {
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
@@ -31,6 +31,8 @@ public class TestSynchronized2 extends IntegrationTest {
@Test
@NotYetImplemented
public void test2() {
useDexInput(); // java bytecode don't add exception handlers
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
@@ -33,6 +33,7 @@ public class TestTryCatch7 extends IntegrationTest {
@Test
public void testNoDebug() {
// useDexInput();
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
@@ -25,6 +25,8 @@ public class TestTryCatchMultiException extends IntegrationTest {
@Test
public void test() {
// printDisassemble();
// setFallback();
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
@@ -14,7 +14,7 @@ public class TestGenerics5 extends IntegrationTest {
public static class TestCls {
private InheritableThreadLocal<Map<String, String>> inheritableThreadLocal;
public void put(String key, String val) {
public void test(String key, String val) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
@@ -19,6 +19,7 @@ public class TestDontInlineThis extends IntegrationTest {
TestCls res;
if (field == 7) {
res = this;
System.out.println();
} else {
res = new TestCls();
}
@@ -5,10 +5,7 @@ import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestVariables5 extends IntegrationTest {
@@ -47,12 +44,10 @@ public class TestVariables5 extends IntegrationTest {
public void test() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, not(containsString("int i2++;")));
assertThat(code, containsOne("int i = 0;"));
assertThat(code, containsOne("&& (i = i + 1) == 2"));
// assertThat(code, containsOne("i++;"));
// assertThat(code, containsOne("if (i == 2) {"));
assertThat(cls)
.code()
.doesNotContain("int i2++;")
.containsOne("int i = 0;")
.containsOneOf("i++;", "&& (i = i + 1) == 2");
}
}