Commit 4c03a424 authored by Skylot's avatar Skylot

improve jadx api

parent 4454e013
package jadx.cli;
import jadx.api.Decompiler;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException;
import java.io.File;
......@@ -15,15 +16,30 @@ public class JadxCLI {
try {
JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
checkArgs(jadxArgs);
Decompiler jadx = new Decompiler(jadxArgs);
jadx.processAndSaveAll();
System.exit(jadx.getErrorsCount());
} catch (Throwable e) {
processAndSave(jadxArgs);
} catch (JadxException e) {
LOG.error(e.getMessage());
System.exit(1);
}
}
private static void processAndSave(JadxCLIArgs jadxArgs) {
try {
Decompiler jadx = new Decompiler(jadxArgs);
jadx.loadFiles(jadxArgs.getInput());
jadx.setOutputDir(jadxArgs.getOutDir());
jadx.save();
LOG.info("done");
} catch (Throwable e) {
LOG.error("jadx error:", e);
}
int errorsCount = ErrorsCounter.getErrorCount();
if (errorsCount != 0) {
ErrorsCounter.printReport();
}
System.exit(errorsCount);
}
private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException {
if (jadxArgs.getInput().isEmpty())
throw new JadxException("Please specify input file");
......
......@@ -141,7 +141,10 @@ public final class JadxCLIArgs implements IJadxArgs {
str.append(' ');
}
@Override
public List<File> getInput() {
return input;
}
public File getOutDir() {
return outputDir;
}
......@@ -150,6 +153,10 @@ public final class JadxCLIArgs implements IJadxArgs {
this.outputDir = outputDir;
}
public boolean isPrintHelp() {
return printHelp;
}
@Override
public int getThreadsCount() {
return threadsCount;
......@@ -166,11 +173,6 @@ public final class JadxCLIArgs implements IJadxArgs {
}
@Override
public List<File> getInput() {
return input;
}
@Override
public boolean isFallbackMode() {
return fallbackMode;
}
......@@ -179,10 +181,4 @@ public final class JadxCLIArgs implements IJadxArgs {
public boolean isVerbose() {
return verbose;
}
@Override
public boolean isPrintHelp() {
return printHelp;
}
}
......@@ -10,11 +10,13 @@ import jadx.core.dex.visitors.SaveCode;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.InputFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
......@@ -28,40 +30,106 @@ import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Jadx API usage example:
* <pre><code>
* Decompiler jadx = new Decompiler();
* jadx.loadFile(new File("classes.dex"));
* jadx.setOutputDir(new File("out"));
* jadx.save();
* </code></pre>
* <p/>
* Instead of 'save()' you can get list of decompiled classes:
* <pre><code>
* for(JavaClass cls : jadx.getClasses()) {
* System.out.println(cls.getCode());
* }
* </code></pre>
*/
public final class Decompiler {
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
private final IJadxArgs args;
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
private File outDir;
private RootNode root;
private List<IDexTreeVisitor> passes;
private int errorsCount;
public Decompiler() {
this.args = new DefaultJadxArgs();
init();
}
public Decompiler(IJadxArgs jadxArgs) {
this.args = jadxArgs;
this.passes = Jadx.getPassesList(args);
init();
}
public void processAndSaveAll() {
try {
loadInput();
parseDex();
ExecutorService ex = saveAll(args.getOutDir());
ex.awaitTermination(100, TimeUnit.DAYS);
LOG.info("done");
} catch (Throwable e) {
LOG.error("jadx error:", e);
} finally {
errorsCount = ErrorsCounter.getErrorCount();
if (errorsCount != 0)
ErrorsCounter.printReport();
public void setOutputDir(File outDir) {
this.outDir = outDir;
init();
}
void init() {
if (outDir == null) {
outDir = new File("jadx-output");
}
this.passes = Jadx.getPassesList(args, outDir);
}
public void loadFile(File file) throws IOException, DecodeException {
setInput(file);
parseDex();
loadFiles(Arrays.asList(file));
}
public void loadFiles(List<File> files) throws IOException, DecodeException {
if (files.isEmpty()) {
throw new JadxRuntimeException("Empty file list");
}
inputFiles.clear();
for (File file : files) {
inputFiles.add(new InputFile(file));
}
parse();
}
public void save() {
try {
ExecutorService ex = getSaveExecutor();
ex.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
LOG.error("Save interrupted", e);
}
}
public ThreadPoolExecutor getSaveExecutor() {
if (root == null) {
throw new JadxRuntimeException("No loaded files");
}
int threadsCount = args.getThreadsCount();
LOG.debug("processing threads count: {}", threadsCount);
ArrayList<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(passes);
SaveCode savePass = new SaveCode(outDir, args);
passList.add(savePass);
LOG.info("processing ...");
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
for (ClassNode cls : root.getClasses(false)) {
if (cls.getCode() == null) {
ProcessClass job = new ProcessClass(cls, passList);
executor.execute(job);
} else {
try {
savePass.visit(cls);
} catch (CodegenException e) {
LOG.error("Can't save class {}", cls, e);
}
}
}
executor.shutdown();
return executor;
}
public List<JavaClass> getClasses() {
......@@ -102,48 +170,10 @@ public final class Decompiler {
}
public int getErrorsCount() {
return errorsCount;
}
public ThreadPoolExecutor saveAll(File dir) {
int threadsCount = args.getThreadsCount();
LOG.debug("processing threads count: {}", threadsCount);
ArrayList<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(passes);
SaveCode savePass = new SaveCode(dir, args);
passList.add(savePass);
LOG.info("processing ...");
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
for (ClassNode cls : root.getClasses(false)) {
if (cls.getCode() == null) {
ProcessClass job = new ProcessClass(cls, passList);
executor.execute(job);
} else {
try {
savePass.visit(cls);
} catch (CodegenException e) {
LOG.error("Can't save class {}", cls, e);
}
}
}
executor.shutdown();
return executor;
}
private void loadInput() throws IOException, DecodeException {
inputFiles.clear();
for (File file : args.getInput()) {
inputFiles.add(new InputFile(file));
}
}
private void setInput(File file) throws IOException, DecodeException {
inputFiles.clear();
inputFiles.add(new InputFile(file));
return ErrorsCounter.getErrorCount();
}
private void parseDex() throws DecodeException {
void parse() throws DecodeException {
ClassInfo.clearCache();
ErrorsCounter.reset();
......
package jadx.api;
public class DefaultJadxArgs implements IJadxArgs {
@Override
public int getThreadsCount() {
return Runtime.getRuntime().availableProcessors();
}
@Override
public boolean isCFGOutput() {
return false;
}
@Override
public boolean isRawCFGOutput() {
return false;
}
@Override
public boolean isFallbackMode() {
return false;
}
@Override
public boolean isVerbose() {
return false;
}
}
package jadx.api;
import java.io.File;
import java.util.List;
public interface IJadxArgs {
List<File> getInput();
File getOutDir();
int getThreadsCount();
boolean isCFGOutput();
......@@ -17,6 +10,4 @@ public interface IJadxArgs {
boolean isFallbackMode();
boolean isVerbose();
boolean isPrintHelp();
}
......@@ -20,6 +20,7 @@ import jadx.core.dex.visitors.regions.RegionMakerVisitor;
import jadx.core.dex.visitors.typeresolver.FinishTypeResolver;
import jadx.core.dex.visitors.typeresolver.TypeResolver;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
......@@ -36,7 +37,7 @@ public class Jadx {
LOG.info("assertions enabled");
}
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args) {
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args, File outDir) {
List<IDexTreeVisitor> passes = new ArrayList<IDexTreeVisitor>();
if (args.isFallbackMode()) {
passes.add(new FallbackModeVisitor());
......@@ -48,13 +49,13 @@ public class Jadx {
passes.add(new FinishTypeResolver());
if (args.isRawCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), false, true));
passes.add(new DotGraphVisitor(outDir, false, true));
passes.add(new ModVisitor());
passes.add(new EnumVisitor());
if (args.isCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), false));
passes.add(new DotGraphVisitor(outDir, false));
passes.add(new RegionMakerVisitor());
passes.add(new PostRegionVisitor());
......@@ -63,7 +64,7 @@ public class Jadx {
passes.add(new ProcessVariables());
passes.add(new CheckRegions());
if (args.isCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), true));
passes.add(new DotGraphVisitor(outDir, true));
passes.add(new MethodInlinerVisitor());
passes.add(new ClassModifier());
......
......@@ -30,9 +30,9 @@ public class JadxWrapper {
try {
this.decompiler.loadFile(file);
} catch (IOException e) {
e.printStackTrace();
LOG.error("Error open file: " + file, e);
} catch (DecodeException e) {
e.printStackTrace();
LOG.error("Error decode file: " + file, e);
}
}
......@@ -41,7 +41,8 @@ public class JadxWrapper {
@Override
public void run() {
try {
ThreadPoolExecutor ex = decompiler.saveAll(dir);
decompiler.setOutputDir(dir);
ThreadPoolExecutor ex = decompiler.getSaveExecutor();
while (ex.isTerminating()) {
long total = ex.getTaskCount();
long done = ex.getCompletedTaskCount();
......
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