core: support 'not-int' and 'not-long' instructions
This commit is contained in:
@@ -9,6 +9,7 @@ dependencies {
|
||||
compile 'com.intellij:annotations:12.0'
|
||||
compile 'uk.com.robust-it:cloning:1.9.2'
|
||||
|
||||
testCompile 'org.smali:smali:2.0.3'
|
||||
testCompile 'org.smali:smali:2.2.2'
|
||||
testCompile 'org.smali:baksmali:2.2.2'
|
||||
}
|
||||
|
||||
|
||||
@@ -270,18 +270,13 @@ public class InsnGen {
|
||||
makeArith((ArithNode) insn, code, state);
|
||||
break;
|
||||
|
||||
case NEG: {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
code.add('-');
|
||||
addArg(code, insn.getArg(0));
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
case NEG:
|
||||
oneArgInsn(code, insn, state, '-');
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
oneArgInsn(code, insn, state, '~');
|
||||
break;
|
||||
}
|
||||
|
||||
case RETURN:
|
||||
if (insn.getArgsCount() != 0) {
|
||||
@@ -525,6 +520,18 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void oneArgInsn(CodeWriter code, InsnNode insn, Set<Flags> state, char op) throws CodegenException {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
code.add(op);
|
||||
addArg(code, insn.getArg(0));
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
}
|
||||
|
||||
private void fallbackOnlyInsn(InsnNode insn) throws CodegenException {
|
||||
if (!fallback) {
|
||||
throw new CodegenException(insn.getType() + " can be used only in fallback mode");
|
||||
|
||||
@@ -324,6 +324,11 @@ public class InsnDecoder {
|
||||
case Opcodes.NEG_DOUBLE:
|
||||
return neg(insn, ArgType.DOUBLE);
|
||||
|
||||
case Opcodes.NOT_INT:
|
||||
return not(insn, ArgType.INT);
|
||||
case Opcodes.NOT_LONG:
|
||||
return not(insn, ArgType.LONG);
|
||||
|
||||
case Opcodes.INT_TO_BYTE:
|
||||
return cast(insn, ArgType.INT, ArgType.BYTE);
|
||||
case Opcodes.INT_TO_CHAR:
|
||||
@@ -699,6 +704,13 @@ public class InsnDecoder {
|
||||
return inode;
|
||||
}
|
||||
|
||||
private InsnNode not(DecodedInstruction insn, ArgType type) {
|
||||
InsnNode inode = new InsnNode(InsnType.NOT, 1);
|
||||
inode.setResult(InsnArg.reg(insn, 0, type));
|
||||
inode.addArg(InsnArg.reg(insn, 1, type));
|
||||
return inode;
|
||||
}
|
||||
|
||||
private InsnNode insn(InsnType type, RegisterArg res) {
|
||||
InsnNode node = new InsnNode(type, 0);
|
||||
node.setResult(res);
|
||||
|
||||
@@ -8,6 +8,7 @@ public enum InsnType {
|
||||
|
||||
ARITH,
|
||||
NEG,
|
||||
NOT,
|
||||
|
||||
MOVE,
|
||||
CAST,
|
||||
|
||||
@@ -81,26 +81,10 @@ public class ErrorsCounter {
|
||||
}
|
||||
|
||||
public static String formatErrorMsg(ClassNode cls, String msg) {
|
||||
return msg + " in class: " + cls;
|
||||
return msg + " in class: " + cls + ", dex: " + cls.dex().getDexFile().getName();
|
||||
}
|
||||
|
||||
public static String formatErrorMsg(MethodNode mth, String msg) {
|
||||
return msg + " in method: " + mth;
|
||||
}
|
||||
|
||||
private String formatException(Throwable e) {
|
||||
if (e == null || e.getMessage() == null) {
|
||||
return "";
|
||||
} else {
|
||||
return "\n error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public String formatErrorMsg(ClassNode cls, String msg, Throwable e) {
|
||||
return formatErrorMsg(cls, msg) + formatException(e);
|
||||
}
|
||||
|
||||
public String formatErrorMsg(MethodNode mth, String msg, Throwable e) {
|
||||
return formatErrorMsg(mth, msg) + formatException(e);
|
||||
return msg + " in method: " + mth + ", dex: " + mth.dex().getDexFile().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import jadx.tests.api.compiler.StaticCompiler;
|
||||
import jadx.tests.api.utils.TestUtils;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.addFileToJar;
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
@@ -261,11 +260,11 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
assertThat("File list is empty", list, not(empty()));
|
||||
|
||||
File temp = createTempFile(".jar");
|
||||
JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp));
|
||||
for (File file : list) {
|
||||
addFileToJar(jo, file, path + "/" + file.getName());
|
||||
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp))) {
|
||||
for (File file : list) {
|
||||
addFileToJar(jo, file, path + "/" + file.getName());
|
||||
}
|
||||
}
|
||||
close(jo);
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -336,13 +335,7 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
outTmp.deleteOnExit();
|
||||
List<File> files = StaticCompiler.compile(compileFileList, outTmp, withDebugInfo);
|
||||
// remove classes which are parents for test class
|
||||
Iterator<File> iterator = files.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
File next = iterator.next();
|
||||
if (!next.getName().contains(cls.getSimpleName())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
files.removeIf(next -> !next.getName().contains(cls.getSimpleName()));
|
||||
for (File clsFile : files) {
|
||||
clsFile.deleteOnExit();
|
||||
}
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
package jadx.tests.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jf.smali.main;
|
||||
import org.jf.smali.Smali;
|
||||
import org.jf.smali.SmaliOptions;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public abstract class SmaliTest extends IntegrationTest {
|
||||
|
||||
private static final String SMALI_TESTS_PROJECT = "jadx-core";
|
||||
private static final String SMALI_TESTS_DIR = "src/test/smali";
|
||||
private static final String SMALI_TESTS_EXT = ".smali";
|
||||
|
||||
protected ClassNode getClassNodeFromSmali(String clsName) {
|
||||
File smaliFile = getSmaliFile(clsName);
|
||||
protected ClassNode getClassNodeFromSmali(String file, String clsName) {
|
||||
File smaliFile = getSmaliFile(file);
|
||||
File outDex = createTempFile(".dex");
|
||||
compileSmali(smaliFile, outDex);
|
||||
return getClassNodeFromFile(outDex, clsName);
|
||||
}
|
||||
|
||||
protected ClassNode getClassNodeFromSmaliWithPath(String path, String clsName) {
|
||||
return getClassNodeFromSmali(path + File.separatorChar + clsName, clsName);
|
||||
}
|
||||
|
||||
protected ClassNode getClassNodeFromSmali(String clsName) {
|
||||
return getClassNodeFromSmali(clsName, clsName);
|
||||
}
|
||||
|
||||
private static File getSmaliFile(String clsName) {
|
||||
File smaliFile = new File(SMALI_TESTS_DIR, clsName + SMALI_TESTS_EXT);
|
||||
if (smaliFile.exists()) {
|
||||
@@ -32,18 +37,17 @@ public abstract class SmaliTest extends IntegrationTest {
|
||||
if (smaliFile.exists()) {
|
||||
return smaliFile;
|
||||
}
|
||||
fail("Smali file not found: " + SMALI_TESTS_DIR + "/" + clsName + SMALI_TESTS_EXT);
|
||||
return null;
|
||||
throw new AssertionError("Smali file not found: " + SMALI_TESTS_DIR + "/" + clsName + SMALI_TESTS_EXT);
|
||||
}
|
||||
|
||||
private static boolean compileSmali(File input, File output) {
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add(input.getAbsolutePath());
|
||||
|
||||
args.add("-o");
|
||||
args.add(output.getAbsolutePath());
|
||||
|
||||
main.main(args.toArray(new String[args.size()]));
|
||||
try {
|
||||
SmaliOptions params = new SmaliOptions();
|
||||
params.outputDexFile = output.getAbsolutePath();
|
||||
Smali.assemble(params, input.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Smali assemble error", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import static javax.tools.JavaCompiler.CompilationTask;
|
||||
|
||||
public class StaticCompiler {
|
||||
|
||||
private static final List<String> COMMON_ARGS = Arrays.asList("-source 1.7 -target 1.7".split(" "));
|
||||
private static final List<String> COMMON_ARGS = Arrays.asList("-source 1.8 -target 1.8".split(" "));
|
||||
|
||||
public static List<File> compile(List<File> files, File outDir, boolean includeDebugInfo) throws IOException {
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package jadx.tests.smali;
|
||||
package jadx.tests.integration.arith;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -13,7 +13,7 @@ public class TestArithConst extends SmaliTest {
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
ClassNode cls = getClassNodeFromSmali("TestArithConst");
|
||||
ClassNode cls = getClassNodeFromSmaliWithPath("arith", "TestArithConst");
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("return i + CONST_INT;"));
|
||||
@@ -0,0 +1,35 @@
|
||||
package jadx.tests.integration.arith;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestArithNot extends SmaliTest {
|
||||
/*
|
||||
Smali Code equivalent:
|
||||
public static class TestCls {
|
||||
public int test1(int a) {
|
||||
return ~a;
|
||||
}
|
||||
|
||||
public long test2(long b) {
|
||||
return ~b;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNodeFromSmaliWithPath("arith", "TestArithNot");
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsString("return ~a;"));
|
||||
assertThat(code, containsString("return ~b;"));
|
||||
assertThat(code, not(containsString("^")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.class public LTestArithNot;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method private test1(I)I
|
||||
.registers 2
|
||||
.param p1, "a"
|
||||
|
||||
not-int v0, p1
|
||||
|
||||
return v0
|
||||
.end method
|
||||
|
||||
.method private test2(J)J
|
||||
.registers 4
|
||||
.param p1, "b"
|
||||
|
||||
not-long v0, p1
|
||||
|
||||
return v0
|
||||
.end method
|
||||
Reference in New Issue
Block a user