Commit 017c6b4d authored by Skylot's avatar Skylot

core tests: compile decompiled code

parent d55cd5fb
......@@ -116,6 +116,11 @@ public class ClassGen {
.remove(AccessFlags.ACC_STATIC);
}
// 'static' modifier not allowed for top classes (not inner)
if (!cls.getClassInfo().isInner()) {
af = af.remove(AccessFlags.ACC_STATIC);
}
annotationGen.addForClass(clsCode);
insertSourceFileInfo(clsCode, cls);
clsCode.startLine(af.makeString());
......
package jadx.tests.api;
import jadx.api.JadxInternalAccess;
import jadx.api.DefaultJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess;
import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
......@@ -12,11 +12,13 @@ import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.FileUtils;
import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.utils.TestUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
......@@ -39,6 +41,9 @@ public abstract class IntegrationTest extends TestUtils {
protected String outDir = "test-out-tmp";
protected boolean compile = true;
private DynamicCompiler dynamicCompiler;
public ClassNode getClassNode(Class<?> clazz) {
try {
File jar = getJarForClass(clazz);
......@@ -67,6 +72,7 @@ public abstract class IntegrationTest extends TestUtils {
// don't unload class
checkCode(cls);
compile(cls);
return cls;
}
......@@ -109,6 +115,52 @@ public abstract class IntegrationTest extends TestUtils {
return null;
}
void compile(ClassNode cls) {
if (!compile) {
return;
}
try {
dynamicCompiler = new DynamicCompiler(cls);
boolean result = dynamicCompiler.compile();
assertTrue("Compilation failed on code: \n\n" + cls.getCode() + "\n", result);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
public Object invoke(String method) throws Exception {
return invoke(method, new Class[0]);
}
public Object invoke(String method, Class[] types, Object... args) {
Method mth = getReflectMethod(method, types);
return invoke(mth, args);
}
public Method getReflectMethod(String method, Class... types) {
assertNotNull("dynamicCompiler not ready", dynamicCompiler);
try {
return dynamicCompiler.getMethod(method, types);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
return null;
}
public Object invoke(Method mth, Object... args) {
assertNotNull("dynamicCompiler not ready", dynamicCompiler);
assertNotNull("unknown method", mth);
try {
return dynamicCompiler.invoke(mth, args);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
return null;
}
public File getJarForClass(Class<?> cls) throws IOException {
String path = cls.getPackage().getName().replace('.', '/');
List<File> list = getClassFilesWithInners(cls);
......@@ -159,6 +211,12 @@ public abstract class IntegrationTest extends TestUtils {
return list;
}
// Try to make test class compilable
@Deprecated
public void disableCompilation() {
this.compile = false;
}
// Use only for debug purpose
@Deprecated
protected void setOutputCFG() {
......
package jadx.tests.api.compiler;
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
private CharSequence content;
public CharSequenceJavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
package jadx.tests.api.compiler;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.io.IOException;
import java.security.SecureClassLoader;
import static javax.tools.JavaFileObject.Kind;
public class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
private JavaClassObject jClsObject;
public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
Kind kind, FileObject sibling) throws IOException {
jClsObject = new JavaClassObject(className, kind);
return jClsObject;
}
@Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] clsBytes = jClsObject.getBytes();
return super.defineClass(name, clsBytes, 0, clsBytes.length);
}
};
}
}
package jadx.tests.api.compiler;
import jadx.core.dex.nodes.ClassNode;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import static javax.tools.JavaCompiler.CompilationTask;
public class DynamicCompiler {
private final ClassNode clsNode;
private JavaFileManager fileManager;
private Object instance;
public DynamicCompiler(ClassNode clsNode) {
this.clsNode = clsNode;
}
public boolean compile() throws Exception {
String fullName = clsNode.getFullName();
String code = clsNode.getCode().toString();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
List<JavaFileObject> jFiles = new ArrayList<JavaFileObject>(1);
jFiles.add(new CharSequenceJavaFileObject(fullName, code));
CompilationTask compilerTask = compiler.getTask(null, fileManager, null, null, null, jFiles);
return Boolean.TRUE.equals(compilerTask.call());
}
private void makeInstance() throws Exception {
String fullName = clsNode.getFullName();
instance = fileManager.getClassLoader(null).loadClass(fullName).newInstance();
if (instance == null) {
throw new NullPointerException("Instantiation failed");
}
}
private Object getInstance() throws Exception {
if (instance == null) {
makeInstance();
}
return instance;
}
public Method getMethod(String method, Class[] types) throws Exception {
return getInstance().getClass().getMethod(method, types);
}
public Object invoke(Method mth, Object... args) throws Exception {
return mth.invoke(getInstance(), args);
}
public Object invoke(String method) throws Exception {
return invoke(method, new Class[0]);
}
public Object invoke(String method, Class[] types, Object... args) throws Exception {
Method mth = getMethod(method, types);
return invoke(mth, args);
}
}
package jadx.tests.api.compiler;
import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
public class JavaClassObject extends SimpleJavaFileObject {
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return bos.toByteArray();
}
@Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
}
......@@ -26,6 +26,7 @@ public class TestWrongCode extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -10,11 +10,11 @@ import static org.junit.Assert.assertThat;
public class TestFieldIncrement2 extends IntegrationTest {
class A {
int f = 5;
}
public static class TestCls {
private static class A {
int f = 5;
}
public A a;
public void test1(int n) {
......
......@@ -16,9 +16,10 @@ public class TestArrayFill2 extends IntegrationTest {
return new int[]{1, a + 1, 2};
}
public int[] test2(int a) {
return new int[]{1, a++, a * 2};
}
// TODO
// public int[] test2(int a) {
// return new int[]{1, a++, a * 2};
// }
}
@Test
......
package jadx.tests.integration.enums;
import jadx.tests.api.IntegrationTest;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import java.lang.reflect.Method;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.countString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class TestSwitchOverEnum extends IntegrationTest {
......@@ -20,8 +23,6 @@ public class TestSwitchOverEnum extends IntegrationTest {
return 1;
case TWO:
return 2;
case THREE:
return 3;
}
return 0;
}
......@@ -35,5 +36,10 @@ public class TestSwitchOverEnum extends IntegrationTest {
assertThat(code, countString(1, "synthetic"));
assertThat(code, countString(2, "switch (c) {"));
assertThat(code, countString(2, "case ONE:"));
Method mth = getReflectMethod("testEnum", Count.class);
assertEquals(1, invoke(mth, Count.ONE));
assertEquals(2, invoke(mth, Count.TWO));
assertEquals(0, invoke(mth, Count.THREE));
}
}
......@@ -40,6 +40,7 @@ public class TestAnonymousClass3 extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -46,6 +46,7 @@ public class TestArrayForEachNegative extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -35,6 +35,7 @@ public class TestSequentialLoops extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -88,6 +88,7 @@ public class TestIssue13a extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -30,6 +30,7 @@ public class TestTryCatch4 extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -43,6 +43,7 @@ public class TestTryCatch5 extends IntegrationTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
......@@ -14,6 +14,7 @@ public class TestConstructor extends SmaliTest {
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNodeFromSmali("TestConstructor");
String code = cls.getCode().toString();
System.out.println(code);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment