From 7f0815a7b2c2f29ae70cec09873ab28d02bb8c10 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 11 Oct 2014 21:01:59 +0400 Subject: [PATCH] core tests: add option for compile test without debug info --- .../src/main/java/jadx/core/clsp/ClsSet.java | 4 +- .../java/jadx/core/codegen/CodeWriter.java | 4 +- .../src/main/java/jadx/core/utils/Utils.java | 14 --- .../java/jadx/core/utils/files/FileUtils.java | 13 +++ .../java/jadx/tests/api/IntegrationTest.java | 67 ++++++++++++++- .../tests/api/compiler/StaticCompiler.java | 86 +++++++++++++++++++ 6 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java index 9043df9e9..ff17fab07 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -3,9 +3,9 @@ package jadx.core.clsp; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.Utils; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.core.utils.files.FileUtils; import java.io.BufferedOutputStream; import java.io.DataInputStream; @@ -102,7 +102,7 @@ public class ClsSet { } void save(File output) throws IOException { - Utils.makeDirsForFile(output); + FileUtils.makeDirsForFile(output); BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output)); try { diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index 9784f5ad5..19c557ffb 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -2,7 +2,7 @@ package jadx.core.codegen; import jadx.api.CodePosition; import jadx.core.dex.attributes.nodes.LineAttrNode; -import jadx.core.utils.Utils; +import jadx.core.utils.files.FileUtils; import java.io.File; import java.io.PrintWriter; @@ -288,7 +288,7 @@ public class CodeWriter { PrintWriter out = null; try { - Utils.makeDirsForFile(file); + FileUtils.makeDirsForFile(file); out = new PrintWriter(file, "UTF-8"); String code = buf.toString(); code = removeFirstEmptyLine(code); diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 5f211bfb4..7d5102af1 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -1,8 +1,5 @@ package jadx.core.utils; -import jadx.core.utils.exceptions.JadxRuntimeException; - -import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Iterator; @@ -93,15 +90,4 @@ public class Utils { throwable.printStackTrace(pw); return sw.getBuffer().toString(); } - - public static void makeDirsForFile(File file) { - File dir = file.getParentFile(); - if (dir != null && !dir.exists()) { - // if directory already created in other thread mkdirs will return false, - // so check dir existence again - if (!dir.mkdirs() && !dir.exists()) { - throw new JadxRuntimeException("Can't create directory " + dir); - } - } - } } diff --git a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java index 5e607d459..c8b7b645e 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java @@ -1,5 +1,7 @@ package jadx.core.utils.files; +import jadx.core.utils.exceptions.JadxRuntimeException; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -35,4 +37,15 @@ public class FileUtils { } } } + + public static void makeDirsForFile(File file) { + File dir = file.getParentFile(); + if (dir != null && !dir.exists()) { + // if directory already created in other thread mkdirs will return false, + // so check dir existence again + if (!dir.mkdirs() && !dir.exists()) { + throw new JadxRuntimeException("Can't create directory " + dir); + } + } + } } 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 f4e45d0b2..d9c5c5168 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -13,6 +13,7 @@ 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.compiler.StaticCompiler; import jadx.tests.api.utils.TestUtils; import java.io.File; @@ -24,12 +25,15 @@ import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.jar.JarOutputStream; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -37,10 +41,15 @@ import static org.junit.Assert.fail; public abstract class IntegrationTest extends TestUtils { + private static final String TEST_DIRECTORY = "src/test/java"; + private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY; + protected boolean outputCFG = false; protected boolean isFallback = false; protected boolean deleteTmpFiles = true; + protected boolean withDebugInfo = true; + protected String outDir = "test-out-tmp"; protected boolean compile = true; @@ -183,7 +192,7 @@ public abstract class IntegrationTest extends TestUtils { try { dynamicCompiler = new DynamicCompiler(cls); boolean result = dynamicCompiler.compile(); - assertTrue("Compilation failed on code: \n\n" + cls.getCode() + "\n", result); + assertTrue("Compilation failed", result); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); @@ -218,7 +227,16 @@ public abstract class IntegrationTest extends TestUtils { public File getJarForClass(Class cls) throws IOException { String path = cls.getPackage().getName().replace('.', '/'); - List list = getClassFilesWithInners(cls); + List list; + if (!withDebugInfo) { + list = compileClass(cls); + } else { + list = getClassFilesWithInners(cls); + if (list.isEmpty()) { + list = compileClass(cls); + } + } + assertNotEquals("File list is empty", 0, list.size()); File temp = createTempFile(".jar"); JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp)); @@ -244,6 +262,18 @@ public abstract class IntegrationTest extends TestUtils { return temp; } + private static File createTempDir(String prefix) throws IOException { + File baseDir = new File(System.getProperty("java.io.tmpdir")); + String baseName = prefix + "-" + System.nanoTime(); + for (int counter = 1; counter < 1000; counter++) { + File tempDir = new File(baseDir, baseName + counter); + if (tempDir.mkdir()) { + return tempDir; + } + } + throw new IOException("Failed to create temp directory"); + } + private List getClassFilesWithInners(Class cls) { List list = new ArrayList(); String pkgName = cls.getPackage().getName(); @@ -266,6 +296,39 @@ public abstract class IntegrationTest extends TestUtils { return list; } + private List compileClass(Class cls) throws IOException { + String fileName = cls.getName(); + int end = fileName.indexOf('$'); + if (end != -1) { + fileName = fileName.substring(0, end); + } + fileName = fileName.replace('.', '/') + ".java"; + File file = new File(TEST_DIRECTORY, fileName); + if (!file.exists()) { + file = new File(TEST_DIRECTORY2, fileName); + } + assertTrue("Test source file not found: " + fileName, file.exists()); + + File outTmp = createTempDir("jadx-tmp-classes"); + outTmp.deleteOnExit(); + List files = StaticCompiler.compile(Arrays.asList(file), outTmp, withDebugInfo); + String filter = outTmp.getAbsolutePath() + File.separator + cls.getName().replace('.', '/'); + Iterator iterator = files.iterator(); + while (iterator.hasNext()) { + File next = iterator.next(); + if (!next.getAbsolutePath().startsWith(filter)) { + iterator.remove(); + } else { + next.deleteOnExit(); + } + } + return files; + } + + public void noDebugInfo() { + this.withDebugInfo = false; + } + // Try to make test class compilable @Deprecated public void disableCompilation() { diff --git a/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java b/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java new file mode 100644 index 000000000..b7fca4d25 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java @@ -0,0 +1,86 @@ +package jadx.tests.api.compiler; + +import jadx.core.utils.files.FileUtils; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static javax.tools.JavaCompiler.CompilationTask; + +public class StaticCompiler { + + private static final List COMMON_ARGS = Arrays.asList("-source 1.6 -target 1.6".split(" ")); + + public static List compile(List files, File outDir, boolean includeDebugInfo) throws IOException { + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); + Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles(files); + + StaticFileManager staticFileManager = new StaticFileManager(fileManager, outDir); + + List options = new ArrayList(); + options.add(includeDebugInfo ? "-g" : "-g:none"); + options.addAll(COMMON_ARGS); + CompilationTask task = compiler.getTask(null, staticFileManager, null, options, null, compilationUnits); + Boolean result = task.call(); + fileManager.close(); + if (Boolean.TRUE.equals(result)) { + return staticFileManager.outputFiles(); + } + return Collections.emptyList(); + } + + private static class StaticFileManager extends ForwardingJavaFileManager { + private List files = new ArrayList(); + private File outDir; + + protected StaticFileManager(StandardJavaFileManager fileManager, File outDir) { + super(fileManager); + this.outDir = outDir; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + if (kind == JavaFileObject.Kind.CLASS) { + File file = new File(outDir, className.replace('.', '/') + ".class"); + files.add(file); + return new ClassFileObject(file, kind); + } + throw new UnsupportedOperationException("Can't save location with kind: " + kind); + } + + public List outputFiles() { + return files; + } + } + + private static class ClassFileObject extends SimpleJavaFileObject { + private File file; + + protected ClassFileObject(File file, Kind kind) { + super(URI.create("file://" + file.getAbsolutePath()), kind); + this.file = file; + } + + @Override + public OutputStream openOutputStream() throws IOException { + FileUtils.makeDirsForFile(file); + return new FileOutputStream(file); + } + } +}