com.googlecode.dex2jar.tools.Dex2jarMultiThreadCmd Maven / Gradle / Ivy
The newest version!
/*
* dex2jar - Tools to work with android .dex and java .class files
* Copyright (c) 2009-2012 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.tools;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import com.googlecode.d2j.dex.LambadaNameSafeClassAdapter;
import com.googlecode.d2j.reader.BaseDexFileReader;
import com.googlecode.d2j.reader.MultiDexFileReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import com.googlecode.d2j.dex.ClassVisitorFactory;
import com.googlecode.d2j.dex.ExDex2Asm;
import com.googlecode.d2j.node.DexClassNode;
import com.googlecode.d2j.node.DexFileNode;
import com.googlecode.d2j.reader.DexFileReader;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;
@BaseCmd.Syntax(cmd = "d2j-mt-dex2jar", syntax = "[options] [file1 ... fileN]", desc = "convert dex to jar")
public class Dex2jarMultiThreadCmd extends BaseCmd {
public static void main(String... args) {
new Dex2jarMultiThreadCmd().doMain(args);
}
@Opt(opt = "mt", longOpt = "multi-thread", description = "concurrent process, default is 4 thread")
private int multiThread = 4;
@Opt(opt = "fl", longOpt = "file-list", description = "a file contains a list of dex to process")
private Path fileList;
@Override
protected void doCommandLine() throws Exception {
List f = new ArrayList<>();
f.addAll(Arrays.asList(remainingArgs));
if (fileList != null) {
f.addAll(Files.readAllLines(fileList, StandardCharsets.UTF_8));
}
if (f.size() < 1) {
throw new HelpException();
}
final ExecutorService executorService = Executors.newFixedThreadPool(multiThread);
final Iterator fileIt = f.iterator();
executorService.submit(new Runnable() {
@Override
public void run() {
if (fileIt.hasNext()) {
String fileName = fileIt.next();
try {
run0(fileName, executorService);
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.submit(this); // run this job again
}
} else {
executorService.shutdown();
}
}
});
executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
private void run0(String fileName, final ExecutorService executorService) throws IOException {
// long baseTS = System.currentTimeMillis();
String baseName = getBaseName(new File(fileName).toPath());
Path currentDir = new File(".").toPath();
Path file = currentDir.resolve(baseName + "-dex2jar.jar");
final Path errorFile = currentDir.resolve(baseName + "-error.zip");
System.err.println("dex2jar " + fileName + " -> " + file);
final BaksmaliBaseDexExceptionHandler exceptionHandler = new BaksmaliBaseDexExceptionHandler();
BaseDexFileReader reader = MultiDexFileReader.open(Files.readAllBytes(new File(fileName).toPath()));
DexFileNode fileNode = new DexFileNode();
try {
reader.accept(fileNode, DexFileReader.SKIP_DEBUG | DexFileReader.IGNORE_READ_EXCEPTION);
} catch (Exception ex) {
exceptionHandler.handleFileException(ex);
throw ex;
}
final FileSystem fs = createZip(file);
final Path dist = fs.getPath("/");
ClassVisitorFactory cvf = new ClassVisitorFactory() {
@Override
public ClassVisitor create(final String name) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
final LambadaNameSafeClassAdapter rca = new LambadaNameSafeClassAdapter(cw);
return new ClassVisitor(Opcodes.ASM4, rca) {
@Override
public void visitEnd() {
super.visitEnd();
String className = rca.getClassName();
byte[] data;
try {
// FIXME handle 'java.lang.RuntimeException: Method code too large!'
data = cw.toByteArray();
} catch (Exception ex) {
System.err.println(String.format("ASM fail to generate .class file: %s", className));
exceptionHandler.handleFileException(ex);
return;
}
try {
Path dist1 = dist.resolve(className + ".class");
BaseCmd.createParentDirectories(dist1);
Files.write(dist1, data);
} catch (IOException e) {
exceptionHandler.handleFileException(e);
}
}
};
}
};
new ExDex2Asm(exceptionHandler) {
@Override
public void convertDex(final DexFileNode fileNode, final ClassVisitorFactory cvf) {
if (fileNode.clzs != null) {
final Map classes = collectClzInfo(fileNode);
final List> results = new ArrayList<>(fileNode.clzs.size());
for (final DexClassNode classNode : fileNode.clzs) {
results.add(executorService.submit(new Runnable() {
@Override
public void run() {
convertClass(fileNode, classNode, cvf, classes);
}
}));
}
executorService.submit(new Runnable() {
@Override
public void run() {
for (Future> result : results) {
try {
result.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
BaksmaliBaseDexExceptionHandler exceptionHandler1 = (BaksmaliBaseDexExceptionHandler) exceptionHandler;
if (exceptionHandler1.hasException()) {
exceptionHandler1.dump(errorFile, new String[0]);
}
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
}.convertDex(fileNode, cvf);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy