Commit 4c03a424 authored by Skylot's avatar Skylot

improve jadx api

parent 4454e013
package jadx.cli; package jadx.cli;
import jadx.api.Decompiler; import jadx.api.Decompiler;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
import java.io.File; import java.io.File;
...@@ -15,15 +16,30 @@ public class JadxCLI { ...@@ -15,15 +16,30 @@ public class JadxCLI {
try { try {
JadxCLIArgs jadxArgs = new JadxCLIArgs(args); JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
checkArgs(jadxArgs); checkArgs(jadxArgs);
Decompiler jadx = new Decompiler(jadxArgs); processAndSave(jadxArgs);
jadx.processAndSaveAll(); } catch (JadxException e) {
System.exit(jadx.getErrorsCount());
} catch (Throwable e) {
LOG.error(e.getMessage()); LOG.error(e.getMessage());
System.exit(1); 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 { private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException {
if (jadxArgs.getInput().isEmpty()) if (jadxArgs.getInput().isEmpty())
throw new JadxException("Please specify input file"); throw new JadxException("Please specify input file");
......
...@@ -141,7 +141,10 @@ public final class JadxCLIArgs implements IJadxArgs { ...@@ -141,7 +141,10 @@ public final class JadxCLIArgs implements IJadxArgs {
str.append(' '); str.append(' ');
} }
@Override public List<File> getInput() {
return input;
}
public File getOutDir() { public File getOutDir() {
return outputDir; return outputDir;
} }
...@@ -150,6 +153,10 @@ public final class JadxCLIArgs implements IJadxArgs { ...@@ -150,6 +153,10 @@ public final class JadxCLIArgs implements IJadxArgs {
this.outputDir = outputDir; this.outputDir = outputDir;
} }
public boolean isPrintHelp() {
return printHelp;
}
@Override @Override
public int getThreadsCount() { public int getThreadsCount() {
return threadsCount; return threadsCount;
...@@ -166,11 +173,6 @@ public final class JadxCLIArgs implements IJadxArgs { ...@@ -166,11 +173,6 @@ public final class JadxCLIArgs implements IJadxArgs {
} }
@Override @Override
public List<File> getInput() {
return input;
}
@Override
public boolean isFallbackMode() { public boolean isFallbackMode() {
return fallbackMode; return fallbackMode;
} }
...@@ -179,10 +181,4 @@ public final class JadxCLIArgs implements IJadxArgs { ...@@ -179,10 +181,4 @@ public final class JadxCLIArgs implements IJadxArgs {
public boolean isVerbose() { public boolean isVerbose() {
return verbose; return verbose;
} }
@Override
public boolean isPrintHelp() {
return printHelp;
}
} }
...@@ -10,11 +10,13 @@ import jadx.core.dex.visitors.SaveCode; ...@@ -10,11 +10,13 @@ import jadx.core.dex.visitors.SaveCode;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.InputFile; import jadx.core.utils.files.InputFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
...@@ -28,40 +30,106 @@ import java.util.concurrent.TimeUnit; ...@@ -28,40 +30,106 @@ import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { public final class Decompiler {
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class); private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
private final IJadxArgs args; private final IJadxArgs args;
private final List<InputFile> inputFiles = new ArrayList<InputFile>(); private final List<InputFile> inputFiles = new ArrayList<InputFile>();
private File outDir;
private RootNode root; private RootNode root;
private List<IDexTreeVisitor> passes; private List<IDexTreeVisitor> passes;
private int errorsCount;
public Decompiler() {
this.args = new DefaultJadxArgs();
init();
}
public Decompiler(IJadxArgs jadxArgs) { public Decompiler(IJadxArgs jadxArgs) {
this.args = jadxArgs; this.args = jadxArgs;
this.passes = Jadx.getPassesList(args); init();
} }
public void processAndSaveAll() { public void setOutputDir(File outDir) {
try { this.outDir = outDir;
loadInput(); init();
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();
} }
void init() {
if (outDir == null) {
outDir = new File("jadx-output");
}
this.passes = Jadx.getPassesList(args, outDir);
} }
public void loadFile(File file) throws IOException, DecodeException { public void loadFile(File file) throws IOException, DecodeException {
setInput(file); loadFiles(Arrays.asList(file));
parseDex(); }
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() { public List<JavaClass> getClasses() {
...@@ -102,48 +170,10 @@ public final class Decompiler { ...@@ -102,48 +170,10 @@ public final class Decompiler {
} }
public int getErrorsCount() { public int getErrorsCount() {
return errorsCount; return ErrorsCounter.getErrorCount();
}
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));
} }
private void parseDex() throws DecodeException { void parse() throws DecodeException {
ClassInfo.clearCache(); ClassInfo.clearCache();
ErrorsCounter.reset(); 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; package jadx.api;
import java.io.File;
import java.util.List;
public interface IJadxArgs { public interface IJadxArgs {
List<File> getInput();
File getOutDir();
int getThreadsCount(); int getThreadsCount();
boolean isCFGOutput(); boolean isCFGOutput();
...@@ -17,6 +10,4 @@ public interface IJadxArgs { ...@@ -17,6 +10,4 @@ public interface IJadxArgs {
boolean isFallbackMode(); boolean isFallbackMode();
boolean isVerbose(); boolean isVerbose();
boolean isPrintHelp();
} }
...@@ -20,6 +20,7 @@ import jadx.core.dex.visitors.regions.RegionMakerVisitor; ...@@ -20,6 +20,7 @@ import jadx.core.dex.visitors.regions.RegionMakerVisitor;
import jadx.core.dex.visitors.typeresolver.FinishTypeResolver; import jadx.core.dex.visitors.typeresolver.FinishTypeResolver;
import jadx.core.dex.visitors.typeresolver.TypeResolver; import jadx.core.dex.visitors.typeresolver.TypeResolver;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -36,7 +37,7 @@ public class Jadx { ...@@ -36,7 +37,7 @@ public class Jadx {
LOG.info("assertions enabled"); 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>(); List<IDexTreeVisitor> passes = new ArrayList<IDexTreeVisitor>();
if (args.isFallbackMode()) { if (args.isFallbackMode()) {
passes.add(new FallbackModeVisitor()); passes.add(new FallbackModeVisitor());
...@@ -48,13 +49,13 @@ public class Jadx { ...@@ -48,13 +49,13 @@ public class Jadx {
passes.add(new FinishTypeResolver()); passes.add(new FinishTypeResolver());
if (args.isRawCFGOutput()) 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 ModVisitor());
passes.add(new EnumVisitor()); passes.add(new EnumVisitor());
if (args.isCFGOutput()) if (args.isCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), false)); passes.add(new DotGraphVisitor(outDir, false));
passes.add(new RegionMakerVisitor()); passes.add(new RegionMakerVisitor());
passes.add(new PostRegionVisitor()); passes.add(new PostRegionVisitor());
...@@ -63,7 +64,7 @@ public class Jadx { ...@@ -63,7 +64,7 @@ public class Jadx {
passes.add(new ProcessVariables()); passes.add(new ProcessVariables());
passes.add(new CheckRegions()); passes.add(new CheckRegions());
if (args.isCFGOutput()) if (args.isCFGOutput())
passes.add(new DotGraphVisitor(args.getOutDir(), true)); passes.add(new DotGraphVisitor(outDir, true));
passes.add(new MethodInlinerVisitor()); passes.add(new MethodInlinerVisitor());
passes.add(new ClassModifier()); passes.add(new ClassModifier());
......
...@@ -30,9 +30,9 @@ public class JadxWrapper { ...@@ -30,9 +30,9 @@ public class JadxWrapper {
try { try {
this.decompiler.loadFile(file); this.decompiler.loadFile(file);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOG.error("Error open file: " + file, e);
} catch (DecodeException e) { } catch (DecodeException e) {
e.printStackTrace(); LOG.error("Error decode file: " + file, e);
} }
} }
...@@ -41,7 +41,8 @@ public class JadxWrapper { ...@@ -41,7 +41,8 @@ public class JadxWrapper {
@Override @Override
public void run() { public void run() {
try { try {
ThreadPoolExecutor ex = decompiler.saveAll(dir); decompiler.setOutputDir(dir);
ThreadPoolExecutor ex = decompiler.getSaveExecutor();
while (ex.isTerminating()) { while (ex.isTerminating()) {
long total = ex.getTaskCount(); long total = ex.getTaskCount();
long done = ex.getCompletedTaskCount(); 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