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 519d71f4a..5303688df 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -87,9 +87,11 @@ public abstract class IntegrationTest extends TestUtils { protected JadxArgs args; - protected boolean withDebugInfo; protected boolean compile; + protected boolean withDebugInfo; protected boolean useEclipseCompiler; + private int targetJavaVersion = 8; + protected Map resMap = Collections.emptyMap(); private boolean allowWarnInCode; @@ -458,7 +460,7 @@ public abstract class IntegrationTest extends TestUtils { List compileFileList = Collections.singletonList(file); Path outTmp = FileUtils.createTempDir("jadx-tmp-classes"); - List files = StaticCompiler.compile(compileFileList, outTmp.toFile(), withDebugInfo, useEclipseCompiler); + List files = StaticCompiler.compile(compileFileList, outTmp.toFile(), withDebugInfo, useEclipseCompiler, targetJavaVersion); files.forEach(File::deleteOnExit); // remove classes which are parents for test class String clsName = clsFullName.substring(clsFullName.lastIndexOf('.') + 1); @@ -493,6 +495,10 @@ public abstract class IntegrationTest extends TestUtils { this.useEclipseCompiler = true; } + protected void useTargetJavaVersion(int version) { + this.targetJavaVersion = version; + } + protected void setFallback() { disableCompilation(); this.args.setFallbackMode(true); diff --git a/jadx-core/src/test/java/jadx/tests/api/compiler/JavaUtils.java b/jadx-core/src/test/java/jadx/tests/api/compiler/JavaUtils.java new file mode 100644 index 000000000..ed392cfe3 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/JavaUtils.java @@ -0,0 +1,28 @@ +package jadx.tests.api.compiler; + +import org.apache.commons.lang3.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JavaUtils { + + private static final Logger LOG = LoggerFactory.getLogger(JavaUtils.class); + + public static final int JAVA_VERSION_INT = getJavaVersionInt(); + + private static int getJavaVersionInt() { + String javaSpecVerStr = SystemUtils.JAVA_SPECIFICATION_VERSION; + if (javaSpecVerStr == null) { + LOG.warn("Unknown current java specification version, use 8 as fallback"); + return 8; // fallback version + } + if (javaSpecVerStr.startsWith("1.")) { + return Integer.parseInt(javaSpecVerStr.substring(2)); + } + return Integer.parseInt(javaSpecVerStr); + } + + public static boolean checkJavaVersion(int requiredVersion) { + return JAVA_VERSION_INT >= requiredVersion; + } +} 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 index a4cd508fb..9aea805ce 100644 --- a/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java @@ -5,7 +5,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -24,10 +23,13 @@ import jadx.core.utils.files.FileUtils; public class StaticCompiler { - private static final List COMMON_ARGS = Arrays.asList("-source 1.8 -target 1.8".split(" ")); + public static List compile(List files, File outDir, boolean includeDebugInfo, + boolean useEclipseCompiler, int javaVersion) throws IOException { + if (!JavaUtils.checkJavaVersion(javaVersion)) { + throw new IllegalArgumentException("Current java version not meet requirement: " + + "current: " + JavaUtils.JAVA_VERSION_INT + ", required: " + javaVersion); + } - public static List compile(List files, File outDir, boolean includeDebugInfo, boolean useEclipseCompiler) - throws IOException { JavaCompiler compiler; if (useEclipseCompiler) { compiler = new EclipseCompiler(); @@ -44,7 +46,12 @@ public class StaticCompiler { List options = new ArrayList<>(); options.add(includeDebugInfo ? "-g" : "-g:none"); - options.addAll(COMMON_ARGS); + String javaVerStr = javaVersion <= 8 ? "1." + javaVersion : Integer.toString(javaVersion); + options.add("-source"); + options.add(javaVerStr); + options.add("-target"); + options.add(javaVerStr); + CompilationTask task = compiler.getTask(null, staticFileManager, null, options, null, compilationUnits); Boolean result = task.call(); fileManager.close(); diff --git a/jadx-plugins/jadx-java-convert/build.gradle b/jadx-plugins/jadx-java-convert/build.gradle index aa6908bab..5af13fb69 100644 --- a/jadx-plugins/jadx-java-convert/build.gradle +++ b/jadx-plugins/jadx-java-convert/build.gradle @@ -7,6 +7,7 @@ dependencies { implementation(project(":jadx-plugins:jadx-dex-input")) implementation(files('lib/dx-1.16.jar')) + implementation('com.android.tools:r8:2.2.64') implementation 'org.ow2.asm:asm:9.2' } diff --git a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java new file mode 100644 index 000000000..a71fe7873 --- /dev/null +++ b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java @@ -0,0 +1,53 @@ +package jadx.plugins.input.javaconvert; + +import java.nio.file.Path; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.D8; +import com.android.tools.r8.D8Command; +import com.android.tools.r8.Diagnostic; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.OutputMode; + +public class D8Converter { + private static final Logger LOG = LoggerFactory.getLogger(D8Converter.class); + + public static void run(Path path, Path tempDirectory) throws CompilationFailedException { + D8Command d8Command = D8Command.builder(new LogHandler()) + .addProgramFiles(path) + .setOutput(tempDirectory, OutputMode.DexIndexed) + .setMode(CompilationMode.DEBUG) + .setMinApiLevel(30) + .setIntermediate(true) + .setDisableDesugaring(true) + .build(); + D8.run(d8Command); + } + + private static class LogHandler implements DiagnosticsHandler { + @Override + public void error(Diagnostic diagnostic) { + LOG.error("D8 error: {}", format(diagnostic)); + } + + @Override + public void warning(Diagnostic diagnostic) { + LOG.warn("D8 warning: {}", format(diagnostic)); + } + + @Override + public void info(Diagnostic diagnostic) { + LOG.info("D8 info: {}", format(diagnostic)); + } + + public static String format(Diagnostic diagnostic) { + return diagnostic.getDiagnosticMessage() + + ", origin: " + diagnostic.getOrigin() + + ", position: " + diagnostic.getPosition(); + } + } +} diff --git a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/DxConverter.java b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/DxConverter.java index 8a9be16d9..f8bef2389 100644 --- a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/DxConverter.java +++ b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/DxConverter.java @@ -43,7 +43,7 @@ public class DxConverter { throw new RuntimeException("dx exception: " + e.getMessage(), e); } if (result != 0) { - throw new RuntimeException("Java to dex conversion error, code: " + result + "\n errors: " + dxErrors); + throw new RuntimeException("Java to dex conversion error, code: " + result + ", errors: " + dxErrors); } } } diff --git a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java index fcba420f7..27fa33891 100644 --- a/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java +++ b/jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java @@ -156,7 +156,12 @@ public class JavaConvertLoader { Path tempDirectory = Files.createTempDirectory("jadx-"); result.addTempPath(tempDirectory); - DxConverter.run(path, tempDirectory); + try { + DxConverter.run(path, tempDirectory); + } catch (Exception e) { + LOG.warn("DX convert failed, trying D8"); + D8Converter.run(path, tempDirectory); + } LOG.debug("Converted to dex: {}", path.toAbsolutePath()); result.addConvertedFiles(collectFilesInDir(tempDirectory));