Commit 948f9456 authored by Skylot's avatar Skylot

core: change jadx args api for easier processing and validation

parent 32f94b46
package jadx.cli;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxException;
public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
......@@ -14,7 +11,7 @@ public class JadxCLI {
public static void main(String[] args) {
try {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (processArgs(jadxArgs, args)) {
if (jadxArgs.processArgs(args)) {
processAndSave(jadxArgs);
}
} catch (Exception e) {
......@@ -23,10 +20,9 @@ public class JadxCLI {
}
}
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
jadx.setOutputDir(jadxArgs.getOutDir());
jadx.loadFiles(jadxArgs.getInput());
static void processAndSave(JadxCLIArgs inputArgs) {
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
jadx.load();
jadx.save();
if (jadx.getErrorsCount() != 0) {
jadx.printErrorsReport();
......@@ -35,34 +31,4 @@ public class JadxCLI {
LOG.info("done");
}
}
static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException {
if (!jadxArgs.processArgs(args)) {
return false;
}
if (jadxArgs.getInput().isEmpty()) {
LOG.error("Please specify input file");
jadxArgs.printUsage();
return false;
}
File outputDir = jadxArgs.getOutDir();
if (outputDir == null) {
String outDirName;
File file = jadxArgs.getInput().get(0);
String name = file.getName();
int pos = name.lastIndexOf('.');
if (pos != -1) {
outDirName = name.substring(0, pos);
} else {
outDirName = name + "-jadx-out";
}
LOG.info("output directory: {}", outDirName);
outputDir = new File(outDirName);
jadxArgs.setOutputDir(outputDir);
}
if (outputDir.exists() && !outputDir.isDirectory()) {
throw new JadxException("Output directory exists as file " + outputDir);
}
return true;
}
}
package jadx.cli;
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
......@@ -18,28 +18,27 @@ import com.beust.jcommander.ParameterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.FileUtils;
public class JadxCLIArgs implements IJadxArgs {
protected static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
public class JadxCLIArgs {
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)")
protected List<String> files;
protected List<String> files = new ArrayList<>(1);
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
protected String outDirName;
protected String outDir;
@Parameter(names = {"-ds", "--output-dir-src"}, description = "output directory for sources")
protected String outDirNameSrc;
protected String outDirSrc;
@Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources")
protected String outDirNameRes;
protected String outDirRes;
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
protected int threadsCount = DEFAULT_THREADS_COUNT;
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
protected boolean skipResources = false;
......@@ -94,11 +93,6 @@ public class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
private final List<File> input = new ArrayList<>(1);
private File outputDir;
private File outputDirSrc;
private File outputDirRes;
public boolean processArgs(String[] args) {
return parse(args) && process();
}
......@@ -123,35 +117,6 @@ public class JadxCLIArgs implements IJadxArgs {
if (threadsCount <= 0) {
throw new JadxException("Threads count must be positive, got: " + threadsCount);
}
if (files != null) {
for (String fileName : files) {
File file = new File(fileName);
if (file.exists()) {
input.add(file);
} else {
throw new JadxException("File not found: " + file);
}
}
}
if (input.size() > 1) {
throw new JadxException("Only one input file is supported");
}
if(outDirNameSrc != null) {
outputDirSrc = new File(outDirNameSrc);
}
if(outDirNameRes != null) {
outputDirRes = new File(outDirNameRes);
}
if (outDirName != null) {
outputDir = new File(outDirName);
if(outputDirSrc == null) {
outputDirSrc = new File(outputDir, "source");
}
if(outputDirRes == null) {
outputDirRes = new File(outputDir, "res");
}
}
if (isVerbose()) {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
......@@ -212,6 +177,31 @@ public class JadxCLIArgs implements IJadxArgs {
}
}
public JadxArgs toJadxArgs() {
JadxArgs args = new JadxArgs();
args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));
args.setOutDir(FileUtils.toFile(outDir));
args.setOutDirSrc(FileUtils.toFile(outDirSrc));
args.setOutDirRes(FileUtils.toFile(outDirRes));
args.setThreadsCount(threadsCount);
args.setSkipSources(skipSources);
args.setSkipResources(skipResources);
args.setFallbackMode(fallbackMode);
args.setShowInconsistentCode(showInconsistentCode);
args.setCfgOutput(cfgOutput);
args.setRawCFGOutput(rawCfgOutput);
args.setReplaceConsts(replaceConsts);
args.setDeobfuscationOn(deobfuscationOn);
args.setDeobfuscationForceSave(deobfuscationForceSave);
args.setDeobfuscationMinLength(deobfuscationMinLength);
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
args.setEscapeUnicode(escapeUnicode);
args.setExportAsGradleProject(exportAsGradleProject);
args.setUseImports(useImports);
return args;
}
public static class InvertedBooleanConverter implements IStringConverter<Boolean> {
@Override
public Boolean convert(String value) {
......@@ -219,114 +209,90 @@ public class JadxCLIArgs implements IJadxArgs {
}
}
public List<File> getInput() {
return input;
public List<String> getFiles() {
return files;
}
@Override
public File getOutDir() {
return outputDir;
}
@Override
public File getOutDirSrc() {
return outputDirSrc;
public String getOutDir() {
return outDir;
}
@Override
public File getOutDirRes() {
return outputDirRes;
public String getOutDirSrc() {
return outDirSrc;
}
public void setOutputDir(File outputDir) {
this.outputDir = outputDir;
public String getOutDirRes() {
return outDirRes;
}
public boolean isPrintHelp() {
return printHelp;
}
@Override
public boolean isSkipResources() {
return skipResources;
}
@Override
public boolean isSkipSources() {
return skipSources;
}
@Override
public int getThreadsCount() {
return threadsCount;
}
@Override
public boolean isCFGOutput() {
return cfgOutput;
}
@Override
public boolean isRawCFGOutput() {
return rawCfgOutput;
}
@Override
public boolean isFallbackMode() {
return fallbackMode;
}
@Override
public boolean isShowInconsistentCode() {
return showInconsistentCode;
}
@Override
public boolean isUsingImports() {
public boolean isUseImports() {
return useImports;
}
@Override
public boolean isVerbose() {
return verbose;
}
@Override
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@Override
public int getDeobfuscationMinLength() {
return deobfuscationMinLength;
}
@Override
public int getDeobfuscationMaxLength() {
return deobfuscationMaxLength;
}
@Override
public boolean isDeobfuscationForceSave() {
return deobfuscationForceSave;
}
@Override
public boolean useSourceNameAsClassAlias() {
public boolean isDeobfuscationUseSourceNameAsAlias() {
return deobfuscationUseSourceNameAsAlias;
}
@Override
public boolean escapeUnicode() {
return escapeUnicode;
}
@Override
public boolean isReplaceConsts() {
return replaceConsts;
}
@Override
public boolean isExportAsGradleProject() {
return exportAsGradleProject;
}
......
......@@ -8,7 +8,7 @@ import static org.junit.Assert.assertThat;
public class JadxCLIArgsTest {
@Test
public void testInvertedBooleanOption() throws Exception {
public void testInvertedBooleanOption() {
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
assertThat(parse("").isReplaceConsts(), is(true));
}
......
package jadx.api;
import java.io.File;
public interface IJadxArgs {
File getOutDir();
File getOutDirSrc();
File getOutDirRes();
int getThreadsCount();
boolean isCFGOutput();
boolean isRawCFGOutput();
boolean isFallbackMode();
boolean isShowInconsistentCode();
boolean isUsingImports();
boolean isVerbose();
boolean isSkipResources();
boolean isSkipSources();
boolean isDeobfuscationOn();
int getDeobfuscationMinLength();
int getDeobfuscationMaxLength();
boolean isDeobfuscationForceSave();
boolean useSourceNameAsClassAlias();
boolean escapeUnicode();
/**
* Replace constant values with static final fields with same value
*/
boolean isReplaceConsts();
/**
* Save as gradle project
*/
boolean isExportAsGradleProject();
}
package jadx.api;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class JadxArgs implements IJadxArgs {
public class JadxArgs {
private File outDir = new File("jadx-output");
private File outDirSrc = new File(outDir, "source");
private File outDirRes = new File(outDir, "res");
private int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
public static final String DEFAULT_OUT_DIR = "jadx-output";
public static final String DEFAULT_SRC_DIR = "sources";
public static final String DEFAULT_RES_DIR = "resources";
private List<File> inputFiles = new ArrayList<>(1);
private File outDir;
private File outDirSrc;
private File outDirRes;
private int threadsCount = DEFAULT_THREADS_COUNT;
private boolean cfgOutput = false;
private boolean rawCFGOutput = false;
private boolean isVerbose = false;
private boolean fallbackMode = false;
private boolean showInconsistentCode = false;
......@@ -32,7 +42,24 @@ public class JadxArgs implements IJadxArgs {
private boolean replaceConsts = true;
private boolean exportAsGradleProject = false;
@Override
public JadxArgs() {
// use default options
}
public void setRootDir(File rootDir) {
setOutDir(rootDir);
setOutDirSrc(new File(rootDir, DEFAULT_SRC_DIR));
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
}
public List<File> getInputFiles() {
return inputFiles;
}
public void setInputFiles(List<File> inputFiles) {
this.inputFiles = inputFiles;
}
public File getOutDir() {
return outDir;
}
......@@ -41,7 +68,6 @@ public class JadxArgs implements IJadxArgs {
this.outDir = outDir;
}
@Override
public File getOutDirSrc() {
return outDirSrc;
}
......@@ -50,7 +76,6 @@ public class JadxArgs implements IJadxArgs {
this.outDirSrc = outDirSrc;
}
@Override
public File getOutDirRes() {
return outDirRes;
}
......@@ -59,7 +84,6 @@ public class JadxArgs implements IJadxArgs {
this.outDirRes = outDirRes;
}
@Override
public int getThreadsCount() {
return threadsCount;
}
......@@ -68,8 +92,7 @@ public class JadxArgs implements IJadxArgs {
this.threadsCount = threadsCount;
}
@Override
public boolean isCFGOutput() {
public boolean isCfgOutput() {
return cfgOutput;
}
......@@ -77,7 +100,6 @@ public class JadxArgs implements IJadxArgs {
this.cfgOutput = cfgOutput;
}
@Override
public boolean isRawCFGOutput() {
return rawCFGOutput;
}
......@@ -86,7 +108,6 @@ public class JadxArgs implements IJadxArgs {
this.rawCFGOutput = rawCFGOutput;
}
@Override
public boolean isFallbackMode() {
return fallbackMode;
}
......@@ -95,7 +116,6 @@ public class JadxArgs implements IJadxArgs {
this.fallbackMode = fallbackMode;
}
@Override
public boolean isShowInconsistentCode() {
return showInconsistentCode;
}
......@@ -104,8 +124,7 @@ public class JadxArgs implements IJadxArgs {
this.showInconsistentCode = showInconsistentCode;
}
@Override
public boolean isUsingImports() {
public boolean isUseImports() {
return useImports;
}
......@@ -113,16 +132,6 @@ public class JadxArgs implements IJadxArgs {
this.useImports = useImports;
}
@Override
public boolean isVerbose() {
return isVerbose;
}
public void setVerbose(boolean verbose) {
isVerbose = verbose;
}
@Override
public boolean isSkipResources() {
return isSkipResources;
}
......@@ -131,7 +140,6 @@ public class JadxArgs implements IJadxArgs {
isSkipResources = skipResources;
}
@Override
public boolean isSkipSources() {
return isSkipSources;
}
......@@ -140,7 +148,6 @@ public class JadxArgs implements IJadxArgs {
isSkipSources = skipSources;
}
@Override
public boolean isDeobfuscationOn() {
return isDeobfuscationOn;
}
......@@ -149,7 +156,6 @@ public class JadxArgs implements IJadxArgs {
isDeobfuscationOn = deobfuscationOn;
}
@Override
public boolean isDeobfuscationForceSave() {
return isDeobfuscationForceSave;
}
......@@ -158,8 +164,7 @@ public class JadxArgs implements IJadxArgs {
isDeobfuscationForceSave = deobfuscationForceSave;
}
@Override
public boolean useSourceNameAsClassAlias() {
public boolean isUseSourceNameAsClassAlias() {
return useSourceNameAsClassAlias;
}
......@@ -167,7 +172,6 @@ public class JadxArgs implements IJadxArgs {
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
}
@Override
public int getDeobfuscationMinLength() {
return deobfuscationMinLength;
}
......@@ -176,7 +180,6 @@ public class JadxArgs implements IJadxArgs {
this.deobfuscationMinLength = deobfuscationMinLength;
}
@Override
public int getDeobfuscationMaxLength() {
return deobfuscationMaxLength;
}
......@@ -185,8 +188,7 @@ public class JadxArgs implements IJadxArgs {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
@Override
public boolean escapeUnicode() {
public boolean isEscapeUnicode() {
return escapeUnicode;
}
......@@ -194,7 +196,6 @@ public class JadxArgs implements IJadxArgs {
this.escapeUnicode = escapeUnicode;
}
@Override
public boolean isReplaceConsts() {
return replaceConsts;
}
......@@ -203,7 +204,6 @@ public class JadxArgs implements IJadxArgs {
this.replaceConsts = replaceConsts;
}
@Override
public boolean isExportAsGradleProject() {
return exportAsGradleProject;
}
......
package jadx.api;
import java.io.File;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class JadxArgsValidator {
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
public static void validate(JadxArgs args) {
if (args.getInputFiles().isEmpty()) {
throw new JadxRuntimeException("Please specify input file");
}
for (File file : args.getInputFiles()) {
checkFile(file);
}
validateOutDirs(args);
}
private static void validateOutDirs(JadxArgs args) {
File outDir = args.getOutDir();
File srcDir = args.getOutDirSrc();
File resDir = args.getOutDirRes();
if (outDir == null) {
if (srcDir != null) {
outDir = srcDir;
} else if (resDir != null) {
outDir = resDir;
} else {
outDir = makeDirFromInput(args);
}
}
args.setOutDir(outDir);
setFromOut(args);
checkDir(args.getOutDir());
checkDir(args.getOutDirSrc());
checkDir(args.getOutDirRes());
}
@NotNull
private static File makeDirFromInput(JadxArgs args) {
File outDir;
String outDirName;
File file = args.getInputFiles().get(0);
String name = file.getName();
int pos = name.lastIndexOf('.');
if (pos != -1) {
outDirName = name.substring(0, pos);
} else {
outDirName = name + "-" + JadxArgs.DEFAULT_OUT_DIR;
}
LOG.info("output directory: {}", outDirName);
outDir = new File(outDirName);
return outDir;
}
private static void setFromOut(JadxArgs args) {
if (args.getOutDirSrc() == null) {
args.setOutDirSrc(new File(args.getOutDir(), JadxArgs.DEFAULT_SRC_DIR));
}
if (args.getOutDirRes() == null) {
args.setOutDirRes(new File(args.getOutDir(), JadxArgs.DEFAULT_RES_DIR));
}
}
private static void checkFile(File file) {
if (!file.exists()) {
throw new JadxRuntimeException("File not found " + file.getAbsolutePath());
}
if (file.isDirectory()) {
throw new JadxRuntimeException("Expected file but found directory instead: " + file.getAbsolutePath());
}
}
private static void checkDir(File dir) {
if (dir != null && dir.exists() && !dir.isDirectory()) {
throw new JadxRuntimeException("Output directory exists as file " + dir);
}
}
private JadxArgsValidator() {
}
}
package jadx.api;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Jadx;
import jadx.core.ProcessClass;
import jadx.core.codegen.CodeGen;
......@@ -11,38 +26,24 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleProject;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ResourcesSaver;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Jadx API usage example:
* <pre><code>
* JadxDecompiler jadx = new JadxDecompiler();
* jadx.loadFile(new File("classes.dex"));
* jadx.setOutputDir(new File("out"));
* JadxArgs args = new JadxArgs();
* args.getInputFiles().add(new File("test.apk"));
* args.setOutDir(new File("jadx-test-output"));
*
* JadxDecompiler jadx = new JadxDecompiler(args);
* jadx.load();
* jadx.save();
* </code></pre>
* <p/>
* Instead of 'save()' you can get list of decompiled classes:
* Instead of 'save()' you can iterate over decompiled classes:
* <pre><code>
* for(JavaClass cls : jadx.getClasses()) {
* System.out.println(cls.getCode());
......@@ -52,12 +53,9 @@ import org.slf4j.LoggerFactory;
public final class JadxDecompiler {
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
private final IJadxArgs args;
private final List<InputFile> inputFiles = new ArrayList<>();
private JadxArgs args;
private File outDir;
private File outDirRes;
private File outDirSrc;
private final List<InputFile> inputFiles = new ArrayList<>();
private RootNode root;
private List<IDexTreeVisitor> passes;
......@@ -72,54 +70,35 @@ public final class JadxDecompiler {
private Map<MethodNode, JavaMethod> methodsMap = new ConcurrentHashMap<>();
private Map<FieldNode, JavaField> fieldsMap = new ConcurrentHashMap<>();
public JadxDecompiler() throws JadxException {
public JadxDecompiler() {
this(new JadxArgs());
}
public JadxDecompiler(IJadxArgs jadxArgs) throws JadxException {
this.args = jadxArgs;
this.outDir = jadxArgs.getOutDir();
this.outDirSrc = jadxArgs.getOutDirSrc();
this.outDirRes = jadxArgs.getOutDirRes();
reset();
init();
public JadxDecompiler(JadxArgs args) {
this.args = args;
}
public void setOutputDir(File outDir) throws JadxException {
this.outDir = outDir;
public void load() {
reset();
JadxArgsValidator.validate(args);
init();
}
LOG.info("loading ...");
public void setOutputDirSrc(File outDirSrc) throws JadxException {
this.outDirSrc = outDirSrc;
init();
}
loadFiles(args.getInputFiles());
public void setOutputDirRes(File outDirRes) throws JadxException {
this.outDirRes = outDirRes;
init();
}
root = new RootNode(args);
root.load(inputFiles);
void init() throws JadxException {
if(outDir == null && outDirSrc == null) {
outDirSrc = new JadxArgs().getOutDirSrc();
}
if(outDir == null && outDirRes == null) {
outDirRes = new JadxArgs().getOutDirRes();
}
if (outDir == null) {
outDir = new JadxArgs().getOutDir();
}
else {
if(outDirSrc == null && outDirRes != null && !args.isSkipSources()) {
throw new JadxException("--output-dir-src must be specified");
}
if(outDirSrc != null && outDirRes == null && !args.isSkipResources()) {
throw new JadxException("--output-dir-res must be specified");
}
root.initClassPath();
root.loadResources(getResources());
root.initAppResClass();
initVisitors();
}
this.passes = Jadx.getPassesList(args, outDir);
this.codeGen = new CodeGen(args);
void init() {
this.passes = Jadx.getPassesList(args);
this.codeGen = new CodeGen();
}
void reset() {
......@@ -135,23 +114,18 @@ public final class JadxDecompiler {
return Jadx.getVersion();
}
public void loadFile(File file) throws JadxException {
loadFiles(Collections.singletonList(file));
}
public void loadFiles(List<File> files) throws JadxException {
private void loadFiles(List<File> files) {
if (files.isEmpty()) {
throw new JadxException("Empty file list");
throw new JadxRuntimeException("Empty file list");
}
inputFiles.clear();
for (File file : files) {
try {
InputFile.addFilesFrom(file, inputFiles, args.isSkipSources());
} catch (IOException e) {
throw new JadxException("Error load file: " + file, e);
} catch (Exception e) {
throw new JadxRuntimeException("Error load file: " + file, e);
}
}
parse();
}
public void save() {
......@@ -194,13 +168,13 @@ public final class JadxDecompiler {
File sourcesOutDir;
File resOutDir;
if (args.isExportAsGradleProject()) {
ExportGradleProject export = new ExportGradleProject(root, outDir);
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir());
export.init();
sourcesOutDir = export.getSrcOutDir();
resOutDir = export.getResOutDir();
} else {
sourcesOutDir = outDirSrc;
resOutDir = outDirRes;
sourcesOutDir = args.getOutDirSrc();
resOutDir = args.getOutDirRes();
}
if (saveSources) {
appendSourcesSave(executor, sourcesOutDir);
......@@ -294,21 +268,6 @@ public final class JadxDecompiler {
root.getErrorsCounter().printReport();
}
void parse() throws JadxException {
reset();
init();
root = new RootNode(args);
LOG.info("loading ...");
root.load(inputFiles);
root.initClassPath();
root.loadResources(getResources());
root.initAppResClass();
initVisitors();
}
private void initVisitors() {
for (IDexTreeVisitor pass : passes) {
try {
......@@ -346,7 +305,7 @@ public final class JadxDecompiler {
return fieldsMap;
}
public IJadxArgs getArgs() {
public JadxArgs getArgs() {
return args;
}
......@@ -354,5 +313,4 @@ public final class JadxDecompiler {
public String toString() {
return "jadx decompiler " + getVersion();
}
}
package jadx.core;
import jadx.api.IJadxArgs;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Manifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.core.dex.visitors.ClassModifier;
import jadx.core.dex.visitors.CodeShrinker;
import jadx.core.dex.visitors.ConstInlineVisitor;
......@@ -33,16 +42,6 @@ import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.dex.visitors.typeinference.FinishTypeInference;
import jadx.core.dex.visitors.typeinference.TypeInference;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Manifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Jadx {
private static final Logger LOG = LoggerFactory.getLogger(Jadx.class);
......@@ -55,7 +54,7 @@ public class Jadx {
}
}
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args, File outDir) {
public static List<IDexTreeVisitor> getPassesList(JadxArgs args) {
List<IDexTreeVisitor> passes = new ArrayList<>();
if (args.isFallbackMode()) {
passes.add(new FallbackModeVisitor());
......@@ -71,7 +70,7 @@ public class Jadx {
passes.add(new TypeInference());
if (args.isRawCFGOutput()) {
passes.add(DotGraphVisitor.dumpRaw(outDir));
passes.add(DotGraphVisitor.dumpRaw());
}
passes.add(new ConstInlineVisitor());
......@@ -83,8 +82,8 @@ public class Jadx {
passes.add(new CodeShrinker());
passes.add(new ReSugarCode());
if (args.isCFGOutput()) {
passes.add(DotGraphVisitor.dump(outDir));
if (args.isCfgOutput()) {
passes.add(DotGraphVisitor.dump());
}
passes.add(new RegionMakerVisitor());
......@@ -95,8 +94,8 @@ public class Jadx {
passes.add(new SimplifyVisitor());
passes.add(new CheckRegions());
if (args.isCFGOutput()) {
passes.add(DotGraphVisitor.dumpRegions(outDir));
if (args.isCfgOutput()) {
passes.add(DotGraphVisitor.dumpRegions());
}
passes.add(new MethodInlineVisitor());
......
......@@ -12,7 +12,7 @@ import java.util.Set;
import com.android.dx.rop.code.AccessFlags;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
......@@ -48,8 +48,8 @@ public class ClassGen {
private final Set<ClassInfo> imports = new HashSet<>();
private int clsDeclLine;
public ClassGen(ClassNode cls, IJadxArgs jadxArgs) {
this(cls, null, jadxArgs.isUsingImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
public ClassGen(ClassNode cls, JadxArgs jadxArgs) {
this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
}
public ClassGen(ClassNode cls, ClassGen parentClsGen) {
......
package jadx.core.codegen;
import jadx.api.IJadxArgs;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.exceptions.CodegenException;
public class CodeGen extends AbstractVisitor {
private final IJadxArgs args;
public CodeGen(IJadxArgs args) {
this.args = args;
}
@Override
public boolean visit(ClassNode cls) throws CodegenException {
ClassGen clsGen = new ClassGen(cls, args);
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
CodeWriter clsCode = clsGen.makeClass();
clsCode.finish();
cls.setCode(clsCode);
return false;
}
}
package jadx.core.deobf;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.SourceFileAttr;
import jadx.core.dex.info.ClassInfo;
......@@ -34,7 +34,7 @@ public class Deobfuscator {
public static final String CLASS_NAME_SEPARATOR = ".";
public static final String INNER_CLASS_SEPARATOR = "$";
private final IJadxArgs args;
private final JadxArgs args;
@NotNull
private final List<DexNode> dexNodes;
private final DeobfPresets deobfPresets;
......@@ -58,13 +58,13 @@ public class Deobfuscator {
private int fldIndex = 0;
private int mthIndex = 0;
public Deobfuscator(IJadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
this.args = args;
this.dexNodes = dexNodes;
this.minLength = args.getDeobfuscationMinLength();
this.maxLength = args.getDeobfuscationMaxLength();
this.useSourceNameAsAlias = args.useSourceNameAsClassAlias();
this.useSourceNameAsAlias = args.isUseSourceNameAsClassAlias();
this.deobfPresets = new DeobfPresets(this, deobfMapFile);
}
......
......@@ -8,7 +8,7 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
......@@ -60,7 +60,7 @@ public class ConstStorage {
private Map<Integer, String> resourcesNames = new HashMap<>();
public ConstStorage(IJadxArgs args) {
public ConstStorage(JadxArgs args) {
this.replaceEnabled = args.isReplaceConsts();
}
......
......@@ -65,7 +65,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
// cache maps
private Map<MethodInfo, MethodNode> mthInfoMap = Collections.emptyMap();
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
public ClassNode(DexNode dex, ClassDef cls) {
this.dex = dex;
this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
try {
......@@ -128,7 +128,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
buildCache();
} catch (Exception e) {
throw new DecodeException("Error decode class: " + clsInfo, e);
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
}
}
......
......@@ -46,7 +46,7 @@ public class DexNode implements IDexNode {
this.dexId = dexId;
}
public void loadClasses() throws DecodeException {
public void loadClasses() {
for (ClassDef cls : dexBuf.classDefs()) {
ClassNode clsNode = new ClassNode(this, cls);
classes.add(clsNode);
......
package jadx.core.dex.nodes;
import jadx.api.IJadxArgs;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.api.ResourcesLoader;
......@@ -11,28 +20,19 @@ import jadx.core.dex.info.InfoStorage;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.StringUtils;
import jadx.core.utils.android.AndroidResourcesUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.DexFile;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResTableParser;
import jadx.core.xmlgen.ResourceStorage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RootNode {
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final IJadxArgs args;
private final JadxArgs args;
private final StringUtils stringUtils;
private final ConstStorage constValues;
private final InfoStorage infoStorage = new InfoStorage();
......@@ -43,13 +43,13 @@ public class RootNode {
private ClassNode appResClass;
private ClspGraph clsp;
public RootNode(IJadxArgs args) {
public RootNode(JadxArgs args) {
this.args = args;
this.stringUtils = new StringUtils(args);
this.constValues = new ConstStorage(args);
}
public void load(List<InputFile> inputFiles) throws DecodeException {
public void load(List<InputFile> inputFiles) {
dexNodes = new ArrayList<>();
for (InputFile input : inputFiles) {
for (DexFile dexFile : input.getDexFiles()) {
......@@ -58,7 +58,7 @@ public class RootNode {
DexNode dexNode = new DexNode(this, dexFile, dexNodes.size());
dexNodes.add(dexNode);
} catch (Exception e) {
throw new DecodeException("Error decode file: " + dexFile, e);
throw new JadxRuntimeException("Error decode file: " + dexFile, e);
}
}
}
......@@ -103,22 +103,22 @@ public class RootNode {
appResClass = AndroidResourcesUtils.searchAppResClass(this);
}
public void initClassPath() throws DecodeException {
public void initClassPath() {
try {
if (this.clsp == null) {
ClspGraph clsp = new ClspGraph();
clsp.load();
ClspGraph newClsp = new ClspGraph();
newClsp.load();
List<ClassNode> classes = new ArrayList<>();
for (DexNode dexNode : dexNodes) {
classes.addAll(dexNode.getClasses());
}
clsp.addApp(classes);
newClsp.addApp(classes);
this.clsp = clsp;
this.clsp = newClsp;
}
} catch (IOException e) {
throw new DecodeException("Error loading classpath", e);
} catch (Exception e) {
throw new JadxRuntimeException("Error loading classpath", e);
}
}
......@@ -188,10 +188,6 @@ public class RootNode {
return appResClass;
}
public IJadxArgs getArgs() {
return args;
}
public StringUtils getStringUtils() {
return stringUtils;
}
......@@ -204,4 +200,7 @@ public class RootNode {
return infoStorage;
}
public JadxArgs getArgs() {
return args;
}
}
......@@ -23,42 +23,33 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DotGraphVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(DotGraphVisitor.class);
private static final String NL = "\\l";
private static final boolean PRINT_DOMINATORS = false;
private final File dir;
private final boolean useRegions;
private final boolean rawInsn;
public static DotGraphVisitor dump(File outDir) {
return new DotGraphVisitor(outDir, false, false);
public static DotGraphVisitor dump() {
return new DotGraphVisitor(false, false);
}
public static DotGraphVisitor dumpRaw(File outDir) {
return new DotGraphVisitor(outDir, false, true);
public static DotGraphVisitor dumpRaw() {
return new DotGraphVisitor(false, true);
}
public static DotGraphVisitor dumpRegions(File outDir) {
return new DotGraphVisitor(outDir, true, false);
public static DotGraphVisitor dumpRegions() {
return new DotGraphVisitor(true, false);
}
public static DotGraphVisitor dumpRawRegions(File outDir) {
return new DotGraphVisitor(outDir, true, true);
public static DotGraphVisitor dumpRawRegions() {
return new DotGraphVisitor(true, true);
}
private DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
this.dir = outDir;
private DotGraphVisitor(boolean useRegions, boolean rawInsn) {
this.useRegions = useRegions;
this.rawInsn = rawInsn;
LOG.debug("DOT {}{}graph dump dir: {}",
useRegions ? "regions " : "", rawInsn ? "raw " : "", outDir.getAbsolutePath());
}
@Override
......@@ -66,12 +57,25 @@ public class DotGraphVisitor extends AbstractVisitor {
if (mth.isNoCode()) {
return;
}
new DumpDotGraph().process(mth);
File outRootDir = mth.root().getArgs().getOutDir();
new DumpDotGraph(outRootDir).process(mth);
}
public void save(File dir, MethodNode mth) {
if (mth.isNoCode()) {
return;
}
new DumpDotGraph(dir).process(mth);
}
private class DumpDotGraph {
private final CodeWriter dot = new CodeWriter();
private final CodeWriter conn = new CodeWriter();
private final File dir;
public DumpDotGraph(File dir) {
this.dir = dir;
}
public void process(MethodNode mth) {
dot.startLine("digraph \"CFG for");
......
......@@ -7,7 +7,7 @@ import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.core.Consts;
import jadx.core.codegen.TypeGen;
import jadx.core.deobf.Deobfuscator;
......@@ -31,8 +31,6 @@ public class RenameVisitor extends AbstractVisitor {
@Override
public void init(RootNode root) {
IJadxArgs args = root.getArgs();
List<DexNode> dexNodes = root.getDexNodes();
if (dexNodes.isEmpty()) {
return;
......@@ -43,6 +41,7 @@ public class RenameVisitor extends AbstractVisitor {
String inputName = FilenameUtils.getBaseName(firstInputFileName);
File deobfMapFile = new File(inputPath, inputName + ".jobf");
JadxArgs args = root.getArgs();
deobfuscator = new Deobfuscator(args, dexNodes, deobfMapFile);
boolean deobfuscationOn = args.isDeobfuscationOn();
if (deobfuscationOn) {
......
......@@ -2,7 +2,7 @@ package jadx.core.dex.visitors;
import java.io.File;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.ClassNode;
......@@ -10,7 +10,7 @@ public class SaveCode {
private SaveCode() {}
public static void save(File dir, IJadxArgs args, ClassNode cls) {
public static void save(File dir, JadxArgs args, ClassNode cls) {
CodeWriter clsCode = cls.getCode();
String fileName = cls.getClassInfo().getFullPath() + ".java";
if (args.isFallbackMode()) {
......
......@@ -44,9 +44,9 @@ public class DebugUtils {
public static void dump(MethodNode mth, String desc) {
File out = new File("test-graph" + desc + "-tmp");
DotGraphVisitor.dump(out).visit(mth);
DotGraphVisitor.dumpRaw(out).visit(mth);
DotGraphVisitor.dumpRegions(out).visit(mth);
DotGraphVisitor.dump().save(out, mth);
DotGraphVisitor.dumpRaw().save(out, mth);
DotGraphVisitor.dumpRegions().save(out, mth);
}
public static void printRegionsWithBlock(MethodNode mth, BlockNode block) {
......
package jadx.core.utils;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
public class StringUtils {
private final boolean escapeUnicode;
public StringUtils(IJadxArgs args) {
this.escapeUnicode = args.escapeUnicode();
public StringUtils(JadxArgs args) {
this.escapeUnicode = args.isEscapeUnicode();
}
public String unescapeString(String str) {
......
......@@ -214,4 +214,12 @@ public class FileUtils {
}
return IOCase.SYSTEM.isCaseSensitive();
}
public static File toFile(String path) {
if (path == null) {
return null;
}
return new File(path);
}
}
package jadx.tests
import jadx.api.IJadxArgs
import jadx.api.JadxDecompiler
import jadx.core.utils.exceptions.JadxException
import jadx.core.utils.exceptions.JadxRuntimeException
import spock.lang.Specification
class TestAPI extends Specification {
def "no loaded files"() {
setup:
def d = new JadxDecompiler()
when:
def classes = d.getClasses()
def packages = d.getPackages()
then:
notThrown(NullPointerException)
classes?.isEmpty()
packages?.isEmpty()
}
def "save with no loaded files"() {
when:
new JadxDecompiler().save()
then:
def e = thrown(JadxRuntimeException)
e.message == "No loaded files"
}
def "load empty files list"() {
when:
new JadxDecompiler().loadFiles(Collections.emptyList())
then:
def e = thrown(JadxException)
e.message == "Empty file list"
}
def "load null"() {
when:
new JadxDecompiler().loadFile(null)
then:
thrown(NullPointerException)
}
def "load missing file"() {
when:
new JadxDecompiler().loadFile(new File("_.dex"))
then:
def e = thrown(JadxException)
e.message == "Error load file: _.dex"
e.cause.class == IOException
}
def "pass decompiler args"() {
setup:
def args = Mock(IJadxArgs)
when:
new JadxDecompiler(args)
then:
noExceptionThrown()
}
def "get errors count for new decompiler"() {
expect:
new JadxDecompiler().getErrorsCount() == 0
}
}
package jadx.api;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.files.FileUtils;
import static jadx.core.utils.files.FileUtils.toFile;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class JadxArgsValidatorOutDirsTest {
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidatorOutDirsTest.class);
public JadxArgs args;
@Test
public void checkAllSet() {
setOutDirs("r", "s", "r");
checkOutDirs("r", "s", "r");
}
@Test
public void checkRootOnly() {
setOutDirs("out", null, null);
checkOutDirs("out", "out/" + JadxArgs.DEFAULT_SRC_DIR, "out/" + JadxArgs.DEFAULT_RES_DIR);
}
@Test
public void checkSrcOnly() {
setOutDirs(null, "src", null);
checkOutDirs("src", "src", "src/" + JadxArgs.DEFAULT_RES_DIR);
}
@Test
public void checkResOnly() {
setOutDirs(null, null, "res");
checkOutDirs("res", "res/" + JadxArgs.DEFAULT_SRC_DIR, "res");
}
@Test
public void checkNone() {
setOutDirs(null, null, null);
String inputFileBase = args.getInputFiles().get(0).getName().replace(".apk", "");
checkOutDirs(inputFileBase,
inputFileBase + "/" + JadxArgs.DEFAULT_SRC_DIR,
inputFileBase + "/" + JadxArgs.DEFAULT_RES_DIR);
}
private void setOutDirs(String outDir, String srcDir, String resDir) {
args = makeArgs();
args.setOutDir(toFile(outDir));
args.setOutDirSrc(toFile(srcDir));
args.setOutDirRes(toFile(resDir));
LOG.debug("Set dirs: out={}, src={}, res={}", outDir, srcDir, resDir);
}
private void checkOutDirs(String outDir, String srcDir, String resDir) {
JadxArgsValidator.validate(args);
LOG.debug("Got dirs: out={}, src={}, res={}", args.getOutDir(), args.getOutDirSrc(), args.getOutDirRes());
assertThat(args.getOutDir(), is(toFile(outDir)));
assertThat(args.getOutDirSrc(), is(toFile(srcDir)));
assertThat(args.getOutDirRes(), is(toFile(resDir)));
}
private JadxArgs makeArgs() {
JadxArgs args = new JadxArgs();
args.getInputFiles().add(FileUtils.createTempFile("some.apk"));
return args;
}
}
package jadx.api;
import java.io.File;
import org.junit.Ignore;
import org.junit.Test;
public class JadxDecompilerTest {
@Test
@Ignore
public void testExampleUsage() {
JadxArgs args = new JadxArgs();
args.getInputFiles().add(new File("test.apk"));
args.setOutDir(new File("jadx-test-output"));
JadxDecompiler jadx = new JadxDecompiler(args);
jadx.load();
jadx.save();
}
// TODO make more tests
}
......@@ -10,7 +10,6 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.JarOutputStream;
......@@ -29,7 +28,6 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxException;
import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.compiler.StaticCompiler;
import jadx.tests.api.utils.TestUtils;
......@@ -65,6 +63,7 @@ public abstract class IntegrationTest extends TestUtils {
public IntegrationTest() {
args = new JadxArgs();
args.setOutDir(new File(outDir));
args.setShowInconsistentCode(true);
args.setThreadsCount(1);
args.setSkipResources(true);
......@@ -84,9 +83,10 @@ public abstract class IntegrationTest extends TestUtils {
public ClassNode getClassNodeFromFile(File file, String clsName) {
JadxDecompiler d = null;
try {
args.setInputFiles(Collections.singletonList(file));
d = new JadxDecompiler(args);
d.loadFile(file);
} catch (JadxException e) {
d.load();
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
......@@ -114,18 +114,18 @@ public abstract class IntegrationTest extends TestUtils {
}
private void decompile(JadxDecompiler jadx, ClassNode cls) {
List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs(), new File(outDir));
ProcessClass.process(cls, passes, new CodeGen(jadx.getArgs()));
List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs());
ProcessClass.process(cls, passes, new CodeGen());
}
private void decompileWithoutUnload(JadxDecompiler d, ClassNode cls) {
cls.load();
List<IDexTreeVisitor> passes = Jadx.getPassesList(d.getArgs(), new File(outDir));
List<IDexTreeVisitor> passes = Jadx.getPassesList(d.getArgs());
for (IDexTreeVisitor visitor : passes) {
DepthTraversal.visit(visitor, cls);
}
try {
new CodeGen(d.getArgs()).visit(cls);
new CodeGen().visit(cls);
} catch (CodegenException e) {
e.printStackTrace();
fail(e.getMessage());
......
package jadx.tests.functional;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
......@@ -24,7 +23,7 @@ public class JadxVisitorsOrderTest {
@Test
public void testOrder() {
List<IDexTreeVisitor> passes = Jadx.getPassesList(new JadxArgs(), new File("out"));
List<IDexTreeVisitor> passes = Jadx.getPassesList(new JadxArgs());
List<String> errors = check(passes);
for (String str : errors) {
......
......@@ -5,7 +5,6 @@ import javax.swing.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxException;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.ui.MainWindow;
......@@ -17,24 +16,17 @@ public class JadxGUI {
public static void main(String[] args) {
try {
LogCollector.register();
final JadxSettings jadxArgs = JadxSettingsAdapter.load();
final JadxSettings settings = JadxSettingsAdapter.load();
// overwrite loaded settings by command line arguments
if (!jadxArgs.processArgs(args)) {
if (!settings.processArgs(args)) {
return;
}
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
MainWindow window = new MainWindow(jadxArgs);
SwingUtilities.invokeLater(() -> {
MainWindow window = new MainWindow(settings);
window.open();
}
catch(JadxException e) {
throw new RuntimeException(e);
}
}
});
} catch (Throwable e) {
} catch (Exception e) {
LOG.error("Error: {}", e.getMessage(), e);
System.exit(1);
}
......
......@@ -2,38 +2,38 @@ package jadx.gui;
import javax.swing.*;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.IJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JavaClass;
import jadx.api.JavaPackage;
import jadx.api.ResourceFile;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.gui.settings.JadxSettings;
public class JadxWrapper {
private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);
private final JadxDecompiler decompiler;
private final JadxSettings settings;
private JadxDecompiler decompiler;
private File openFile;
public JadxWrapper(IJadxArgs jadxArgs) throws JadxException {
this.decompiler = new JadxDecompiler(jadxArgs);
public JadxWrapper(JadxSettings settings) {
this.settings = settings;
}
public void openFile(File file) {
this.openFile = file;
try {
this.decompiler.loadFile(file);
} catch (DecodeException e) {
LOG.error("Error decode file: {}", file, e);
} catch (JadxException e) {
LOG.error("Error open file: {}", file, e);
this.decompiler = new JadxDecompiler(settings.toJadxArgs());
this.decompiler.getArgs().setInputFiles(Collections.singletonList(file));
this.decompiler.load();
} catch (Exception e) {
LOG.error("Error load file: {}", file, e);
}
}
......@@ -42,7 +42,7 @@ public class JadxWrapper {
@Override
public void run() {
try {
decompiler.setOutputDir(dir);
decompiler.getArgs().setRootDir(dir);
ThreadPoolExecutor ex = (ThreadPoolExecutor) decompiler.getSaveExecutor();
ex.shutdown();
while (ex.isTerminating()) {
......@@ -53,7 +53,7 @@ public class JadxWrapper {
}
progressMonitor.close();
LOG.info("done");
} catch (InterruptedException|JadxException e) {
} catch (InterruptedException e) {
LOG.error("Save interrupted", e);
Thread.currentThread().interrupt();
}
......
......@@ -11,6 +11,7 @@ import java.util.Set;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.api.JadxArgs;
import jadx.cli.JadxCLIArgs;
public class JadxSettings extends JadxCLIArgs {
......@@ -44,7 +45,7 @@ public class JadxSettings extends JadxCLIArgs {
public void fixOnLoad() {
if (threadsCount <= 0) {
threadsCount = DEFAULT_THREADS_COUNT;
threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
}
}
......@@ -165,8 +166,8 @@ public class JadxSettings extends JadxCLIArgs {
this.deobfuscationForceSave = deobfuscationForceSave;
}
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsAlias) {
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
public void setDeobfuscationUseSourceNameAsAlias(boolean deobfuscationUseSourceNameAsAlias) {
this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias;
}
public void setEscapeUnicode(boolean escapeUnicode) {
......
......@@ -145,10 +145,10 @@ public class JadxSettingsWindow extends JDialog {
});
JCheckBox deobfSourceAlias = new JCheckBox();
deobfSourceAlias.setSelected(settings.useSourceNameAsClassAlias());
deobfSourceAlias.setSelected(settings.isDeobfuscationUseSourceNameAsAlias());
deobfSourceAlias.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setUseSourceNameAsClassAlias(e.getStateChange() == ItemEvent.SELECTED);
settings.setDeobfuscationUseSourceNameAsAlias(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
});
......@@ -219,6 +219,7 @@ public class JadxSettingsWindow extends JDialog {
@Override
public void stateChanged(ChangeEvent e) {
settings.setThreadsCount((Integer) threadsCount.getValue());
needReload();
}
});
......
......@@ -27,12 +27,13 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile;
import jadx.core.utils.exceptions.JadxException;
import jadx.gui.JadxWrapper;
import jadx.gui.jobs.BackgroundWorker;
import jadx.gui.jobs.DecompileJob;
......@@ -101,7 +102,7 @@ public class MainWindow extends JFrame {
private transient ProgressPanel progressPane;
private transient BackgroundWorker backgroundWorker;
public MainWindow(JadxSettings settings) throws JadxException {
public MainWindow(JadxSettings settings) {
this.wrapper = new JadxWrapper(settings);
this.settings = settings;
this.cacheObject = new CacheObject();
......@@ -119,10 +120,10 @@ public class MainWindow extends JFrame {
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
if (settings.getInput().isEmpty()) {
if (settings.getFiles().isEmpty()) {
openFile();
} else {
openFile(settings.getInput().get(0));
openFile(new File(settings.getFiles().get(0)));
}
}
......@@ -689,5 +690,4 @@ public class MainWindow extends JFrame {
public void menuCanceled(MenuEvent e) {
}
}
}
......@@ -7,7 +7,7 @@ import org.junit.Before;
import org.junit.Test;
import jadx.api.Factory;
import jadx.api.IJadxArgs;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JavaClass;
import jadx.api.JavaPackage;
......@@ -30,7 +30,7 @@ public class JSourcesTest {
when(root.isFlatPackages()).thenReturn(false);
JadxWrapper wrapper = mock(JadxWrapper.class);
sources = new JSources(root, wrapper);
decompiler = new JadxDecompiler(mock(IJadxArgs.class));
decompiler = new JadxDecompiler(new JadxArgs());
}
@Test
......
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