未验证 提交 502fd069 编写于 作者: S Skylot

test: for source auto check use compiled classes instead runtime

上级 fad9e7b8
...@@ -16,6 +16,7 @@ import org.slf4j.Logger; ...@@ -16,6 +16,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs; import jadx.api.JadxArgs;
import jadx.api.args.DeobfuscationMapFileMode;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo; import jadx.core.dex.info.MethodInfo;
...@@ -38,7 +39,9 @@ public class DeobfPresets { ...@@ -38,7 +39,9 @@ public class DeobfPresets {
public static DeobfPresets build(RootNode root) { public static DeobfPresets build(RootNode root) {
Path deobfMapPath = getPathDeobfMapPath(root); Path deobfMapPath = getPathDeobfMapPath(root);
LOG.debug("Deobfuscation map file set to: {}", deobfMapPath); if (root.getArgs().getDeobfuscationMapFileMode() != DeobfuscationMapFileMode.IGNORE) {
LOG.debug("Deobfuscation map file set to: {}", deobfMapPath);
}
return new DeobfPresets(deobfMapPath); return new DeobfPresets(deobfMapPath);
} }
......
package jadx.tests.api; package jadx.tests.api;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
...@@ -34,6 +35,7 @@ import jadx.api.ICodeWriter; ...@@ -34,6 +35,7 @@ import jadx.api.ICodeWriter;
import jadx.api.JadxArgs; import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess; import jadx.api.JadxInternalAccess;
import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.data.annotations.InsnCodeOffset; import jadx.api.data.annotations.InsnCodeOffset;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
...@@ -49,9 +51,8 @@ import jadx.core.utils.files.FileUtils; ...@@ -49,9 +51,8 @@ import jadx.core.utils.files.FileUtils;
import jadx.core.xmlgen.ResourceStorage; import jadx.core.xmlgen.ResourceStorage;
import jadx.core.xmlgen.entry.ResourceEntry; import jadx.core.xmlgen.entry.ResourceEntry;
import jadx.tests.api.compiler.CompilerOptions; import jadx.tests.api.compiler.CompilerOptions;
import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.compiler.JavaUtils; import jadx.tests.api.compiler.JavaUtils;
import jadx.tests.api.compiler.StaticCompiler; import jadx.tests.api.compiler.TestCompiler;
import jadx.tests.api.utils.TestUtils; import jadx.tests.api.utils.TestUtils;
import static org.apache.commons.lang3.StringUtils.leftPad; import static org.apache.commons.lang3.StringUtils.leftPad;
...@@ -106,7 +107,8 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -106,7 +107,8 @@ public abstract class IntegrationTest extends TestUtils {
private boolean printDisassemble; private boolean printDisassemble;
private Boolean useJavaInput = null; private Boolean useJavaInput = null;
private DynamicCompiler dynamicCompiler; private @Nullable TestCompiler sourceCompiler;
private @Nullable TestCompiler decompiledCompiler;
static { static {
// enable debug checks // enable debug checks
...@@ -128,13 +130,21 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -128,13 +130,21 @@ public abstract class IntegrationTest extends TestUtils {
args.setSkipResources(true); args.setSkipResources(true);
args.setFsCaseSensitive(false); // use same value on all systems args.setFsCaseSensitive(false); // use same value on all systems
args.setCommentsLevel(CommentsLevel.DEBUG); args.setCommentsLevel(CommentsLevel.DEBUG);
args.setDeobfuscationOn(false);
args.setDeobfuscationMapFileMode(DeobfuscationMapFileMode.IGNORE);
} }
@AfterEach @AfterEach
public void after() { public void after() throws IOException {
FileUtils.clearTempRootDir(); FileUtils.clearTempRootDir();
if (jadxDecompiler != null) { close(jadxDecompiler);
jadxDecompiler.close(); close(sourceCompiler);
close(decompiledCompiler);
}
private void close(Closeable cloaseble) throws IOException {
if (cloaseble != null) {
cloaseble.close();
} }
} }
...@@ -340,16 +350,20 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -340,16 +350,20 @@ public abstract class IntegrationTest extends TestUtils {
} }
private boolean runSourceAutoCheck(String clsName) { private boolean runSourceAutoCheck(String clsName) {
if (sourceCompiler == null) {
// no source code (smali case)
return true;
}
Class<?> origCls; Class<?> origCls;
try { try {
origCls = Class.forName(clsName); origCls = sourceCompiler.getClass(clsName);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// ignore rethrow("Missing class: " + clsName, e);
return true; return true;
} }
Method checkMth; Method checkMth;
try { try {
checkMth = origCls.getMethod(CHECK_METHOD_NAME); checkMth = sourceCompiler.getMethod(origCls, CHECK_METHOD_NAME, new Class[] {});
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// ignore // ignore
return true; return true;
...@@ -371,10 +385,10 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -371,10 +385,10 @@ public abstract class IntegrationTest extends TestUtils {
public void runDecompiledAutoCheck(ClassNode cls) { public void runDecompiledAutoCheck(ClassNode cls) {
try { try {
limitExecTime(() -> invoke(cls, "check")); limitExecTime(() -> invoke(decompiledCompiler, cls.getFullName(), CHECK_METHOD_NAME));
System.out.println("Decompiled check: PASSED"); System.out.println("Decompiled check: PASSED");
} catch (Throwable e) { } catch (Throwable e) {
throw new JadxRuntimeException("Decompiled check failed", e); rethrow("Decompiled check failed", e);
} }
} }
...@@ -398,8 +412,9 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -398,8 +412,9 @@ public abstract class IntegrationTest extends TestUtils {
if (e instanceof InvocationTargetException) { if (e instanceof InvocationTargetException) {
rethrow(msg, e.getCause()); rethrow(msg, e.getCause());
} else if (e instanceof ExecutionException) { } else if (e instanceof ExecutionException) {
rethrow(e.getMessage(), e.getCause()); rethrow(msg, e.getCause());
} else if (e instanceof AssertionError) { } else if (e instanceof AssertionError) {
System.err.println(msg);
throw (AssertionError) e; throw (AssertionError) e;
} else { } else {
throw new RuntimeException(msg, e); throw new RuntimeException(msg, e);
...@@ -421,8 +436,8 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -421,8 +436,8 @@ public abstract class IntegrationTest extends TestUtils {
return; return;
} }
try { try {
dynamicCompiler = new DynamicCompiler(clsList); decompiledCompiler = new TestCompiler(compilerOptions);
boolean result = dynamicCompiler.compile(compilerOptions); boolean result = decompiledCompiler.compileNodes(clsList);
assertTrue(result, "Compilation failed"); assertTrue(result, "Compilation failed");
System.out.println("Compilation: PASSED"); System.out.println("Compilation: PASSED");
} catch (Exception e) { } catch (Exception e) {
...@@ -430,13 +445,9 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -430,13 +445,9 @@ public abstract class IntegrationTest extends TestUtils {
} }
} }
public Object invoke(ClassNode cls, String method) throws Exception { public Object invoke(TestCompiler compiler, String clsFullName, String method) throws Exception {
return invoke(cls, method, new Class<?>[0]); assertNotNull(compiler, "compiler not ready");
} return compiler.invoke(clsFullName, method, new Class<?>[] {}, new Object[] {});
public Object invoke(ClassNode cls, String methodName, Class<?>[] types, Object... args) throws Exception {
assertNotNull(dynamicCompiler, "dynamicCompiler not ready");
return dynamicCompiler.invoke(cls, methodName, types, args);
} }
private List<File> compileClass(Class<?> cls) throws IOException { private List<File> compileClass(Class<?> cls) throws IOException {
...@@ -457,8 +468,8 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -457,8 +468,8 @@ public abstract class IntegrationTest extends TestUtils {
List<File> compileFileList = Collections.singletonList(file); List<File> compileFileList = Collections.singletonList(file);
Path outTmp = FileUtils.createTempDir("jadx-tmp-classes"); Path outTmp = FileUtils.createTempDir("jadx-tmp-classes");
List<File> files = StaticCompiler.compile(compileFileList, outTmp.toFile(), compilerOptions); sourceCompiler = new TestCompiler(compilerOptions);
files.forEach(File::deleteOnExit); List<File> files = sourceCompiler.compileFiles(compileFileList, outTmp);
if (saveTestJar) { if (saveTestJar) {
saveToJar(files, outTmp); saveToJar(files, outTmp);
} }
...@@ -523,7 +534,7 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -523,7 +534,7 @@ public abstract class IntegrationTest extends TestUtils {
protected void enableDeobfuscation() { protected void enableDeobfuscation() {
args.setDeobfuscationOn(true); args.setDeobfuscationOn(true);
args.setDeobfuscationForceSave(true); args.setDeobfuscationMapFileMode(DeobfuscationMapFileMode.OVERWRITE);
args.setDeobfuscationMinLength(2); args.setDeobfuscationMinLength(2);
args.setDeobfuscationMaxLength(64); args.setDeobfuscationMaxLength(64);
} }
......
package jadx.tests.api.compiler; package jadx.tests.api.compiler;
import java.security.SecureClassLoader; import java.io.Closeable;
import java.util.Map; import java.io.File;
import java.util.concurrent.ConcurrentHashMap; import java.util.ArrayList;
import java.util.List;
import javax.tools.FileObject; import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager; import javax.tools.ForwardingJavaFileManager;
...@@ -11,19 +12,27 @@ import javax.tools.StandardJavaFileManager; ...@@ -11,19 +12,27 @@ import javax.tools.StandardJavaFileManager;
import static javax.tools.JavaFileObject.Kind; import static javax.tools.JavaFileObject.Kind;
public class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> { public class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> implements Closeable {
private DynamicClassLoader classLoader; private final DynamicClassLoader classLoader;
public ClassFileManager(StandardJavaFileManager standardManager) { public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager); super(standardManager);
classLoader = new DynamicClassLoader(); classLoader = new DynamicClassLoader();
} }
public List<JavaFileObject> getJavaFileObjectsFromFiles(List<File> sourceFiles) {
List<JavaFileObject> list = new ArrayList<>();
for (JavaFileObject javaFileObject : fileManager.getJavaFileObjectsFromFiles(sourceFiles)) {
list.add(javaFileObject);
}
return list;
}
@Override @Override
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) { public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) {
JavaClassObject clsObject = new JavaClassObject(className, kind); JavaClassObject clsObject = new JavaClassObject(className, kind);
classLoader.getClsMap().put(className, clsObject); classLoader.add(className, clsObject);
return clsObject; return clsObject;
} }
...@@ -32,44 +41,7 @@ public class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFile ...@@ -32,44 +41,7 @@ public class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFile
return classLoader; return classLoader;
} }
private class DynamicClassLoader extends SecureClassLoader { public DynamicClassLoader getClassLoader() {
private final Map<String, JavaClassObject> clsMap = new ConcurrentHashMap<>(); return classLoader;
private final Map<String, Class<?>> clsCache = new ConcurrentHashMap<>();
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> cls = replaceClass(name);
if (cls != null) {
return cls;
}
return super.findClass(name);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> cls = replaceClass(name);
if (cls != null) {
return cls;
}
return super.loadClass(name);
}
public Class<?> replaceClass(String name) throws ClassNotFoundException {
Class<?> cacheCls = clsCache.get(name);
if (cacheCls != null) {
return cacheCls;
}
JavaClassObject clsObject = clsMap.get(name);
if (clsObject == null) {
return null;
}
byte[] clsBytes = clsObject.getBytes();
Class<?> cls = super.defineClass(name, clsBytes, 0, clsBytes.length);
clsCache.put(name, cls);
return cls;
}
public Map<String, JavaClassObject> getClsMap() {
return clsMap;
}
} }
} }
package jadx.tests.api.compiler;
import java.security.SecureClassLoader;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
public class DynamicClassLoader extends SecureClassLoader {
private final Map<String, JavaClassObject> clsMap = new ConcurrentHashMap<>();
private final Map<String, Class<?>> clsCache = new ConcurrentHashMap<>();
public void add(String className, JavaClassObject clsObject) {
this.clsMap.put(className, clsObject);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> cls = replaceClass(name);
if (cls != null) {
return cls;
}
return super.findClass(name);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> cls = replaceClass(name);
if (cls != null) {
return cls;
}
return super.loadClass(name);
}
@Nullable
public Class<?> replaceClass(String name) {
Class<?> cacheCls = clsCache.get(name);
if (cacheCls != null) {
return cacheCls;
}
JavaClassObject clsObject = clsMap.get(name);
if (clsObject == null) {
return null;
}
byte[] clsBytes = clsObject.getBytes();
Class<?> cls = super.defineClass(name, clsBytes, 0, clsBytes.length);
clsCache.put(name, cls);
return cls;
}
public Collection<? extends JavaClassObject> getClassObjects() {
return clsMap.values();
}
}
package jadx.tests.api.compiler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static javax.tools.JavaCompiler.CompilationTask;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class DynamicCompiler {
private static final Logger LOG = LoggerFactory.getLogger(DynamicCompiler.class);
private final List<ClassNode> clsNodeList;
private JavaFileManager fileManager;
public DynamicCompiler(List<ClassNode> clsNodeList) {
this.clsNodeList = clsNodeList;
}
public boolean compile(CompilerOptions compilerOptions) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
LOG.error("Can not find compiler, please use JDK instead");
return false;
}
fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
List<JavaFileObject> jFiles = new ArrayList<>(clsNodeList.size());
for (ClassNode clsNode : clsNodeList) {
jFiles.add(new CharSequenceJavaFileObject(clsNode.getFullName(), clsNode.getCode().toString()));
}
CompilationTask compilerTask = compiler.getTask(null, fileManager, null, compilerOptions.getArguments(), null, jFiles);
return Boolean.TRUE.equals(compilerTask.call());
}
private ClassLoader getClassLoader() {
return fileManager.getClassLoader(null);
}
public Object makeInstance(ClassNode cls) throws Exception {
String fullName = cls.getFullName();
return getClassLoader().loadClass(fullName).getConstructor().newInstance();
}
@NotNull
public Method getMethod(Object inst, String methodName, Class<?>[] types) throws Exception {
for (Class<?> type : types) {
checkType(type);
}
return inst.getClass().getMethod(methodName, types);
}
public Object invoke(ClassNode cls, String methodName, Class<?>[] types, Object[] args) {
try {
Object inst = makeInstance(cls);
Method reflMth = getMethod(inst, methodName, types);
assertNotNull(reflMth, "Failed to get method " + methodName + '(' + Arrays.toString(types) + ')');
return reflMth.invoke(inst, args);
} catch (Throwable e) {
IntegrationTest.rethrow("Invoke error", e);
return null;
}
}
private Class<?> checkType(Class<?> type) throws ClassNotFoundException {
if (type.isPrimitive()) {
return type;
}
if (type.isArray()) {
return checkType(type.getComponentType());
}
Class<?> decompiledCls = getClassLoader().loadClass(type.getName());
if (type != decompiledCls) {
throw new IllegalArgumentException("Internal test class cannot be used in method invoke");
}
return decompiledCls;
}
}
package jadx.tests.api.compiler; package jadx.tests.api.compiler;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URI; import java.net.URI;
...@@ -9,10 +8,17 @@ import javax.tools.SimpleJavaFileObject; ...@@ -9,10 +8,17 @@ import javax.tools.SimpleJavaFileObject;
public class JavaClassObject extends SimpleJavaFileObject { public class JavaClassObject extends SimpleJavaFileObject {
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream(); private final String name;
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
public JavaClassObject(String name, Kind kind) { public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind); super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
this.name = name;
}
@Override
public String getName() {
return name;
} }
public byte[] getBytes() { public byte[] getBytes() {
...@@ -20,7 +26,7 @@ public class JavaClassObject extends SimpleJavaFileObject { ...@@ -20,7 +26,7 @@ public class JavaClassObject extends SimpleJavaFileObject {
} }
@Override @Override
public OutputStream openOutputStream() throws IOException { public OutputStream openOutputStream() {
return bos; return bos;
} }
} }
...@@ -10,6 +10,10 @@ public class JavaUtils { ...@@ -10,6 +10,10 @@ public class JavaUtils {
public static final int JAVA_VERSION_INT = getJavaVersionInt(); public static final int JAVA_VERSION_INT = getJavaVersionInt();
public static boolean checkJavaVersion(int requiredVersion) {
return JAVA_VERSION_INT >= requiredVersion;
}
private static int getJavaVersionInt() { private static int getJavaVersionInt() {
String javaSpecVerStr = SystemUtils.JAVA_SPECIFICATION_VERSION; String javaSpecVerStr = SystemUtils.JAVA_SPECIFICATION_VERSION;
if (javaSpecVerStr == null) { if (javaSpecVerStr == null) {
...@@ -21,8 +25,4 @@ public class JavaUtils { ...@@ -21,8 +25,4 @@ public class JavaUtils {
} }
return Integer.parseInt(javaSpecVerStr); return Integer.parseInt(javaSpecVerStr);
} }
public static boolean checkJavaVersion(int requiredVersion) {
return JAVA_VERSION_INT >= requiredVersion;
}
} }
...@@ -4,11 +4,11 @@ import java.net.URI; ...@@ -4,11 +4,11 @@ import java.net.URI;
import javax.tools.SimpleJavaFileObject; import javax.tools.SimpleJavaFileObject;
public class CharSequenceJavaFileObject extends SimpleJavaFileObject { public class StringJavaFileObject extends SimpleJavaFileObject {
private CharSequence content; private final String content;
public CharSequenceJavaFileObject(String className, CharSequence content) { public StringJavaFileObject(String className, String content) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content; this.content = content;
} }
......
package jadx.tests.api.compiler; package jadx.tests.api.compiler;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; import javax.tools.ToolProvider;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import org.jetbrains.annotations.NotNull;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.files.FileUtils; import jadx.core.utils.files.FileUtils;
import jadx.tests.api.IntegrationTest;
public class StaticCompiler { import static org.junit.jupiter.api.Assertions.assertNotNull;
public static List<File> compile(List<File> files, File outDir, CompilerOptions options) throws IOException { public class TestCompiler implements Closeable {
private final CompilerOptions options;
private final JavaCompiler compiler;
private final ClassFileManager fileManager;
public TestCompiler(CompilerOptions options) {
this.options = options;
int javaVersion = options.getJavaVersion(); int javaVersion = options.getJavaVersion();
if (!JavaUtils.checkJavaVersion(javaVersion)) { if (!JavaUtils.checkJavaVersion(javaVersion)) {
throw new IllegalArgumentException("Current java version not meet requirement: " throw new IllegalArgumentException("Current java version not meet requirement: "
+ "current: " + JavaUtils.JAVA_VERSION_INT + ", required: " + javaVersion); + "current: " + JavaUtils.JAVA_VERSION_INT + ", required: " + javaVersion);
} }
JavaCompiler compiler;
if (options.isUseEclipseCompiler()) { if (options.isUseEclipseCompiler()) {
compiler = new EclipseCompiler(); compiler = new EclipseCompiler();
} else { } else {
...@@ -41,13 +45,37 @@ public class StaticCompiler { ...@@ -41,13 +45,37 @@ public class StaticCompiler {
throw new IllegalStateException("Can not find compiler, please use JDK instead"); throw new IllegalStateException("Can not find compiler, please use JDK instead");
} }
} }
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(files); }
StaticFileManager staticFileManager = new StaticFileManager(fileManager, outDir); public List<File> compileFiles(List<File> sourceFiles, Path outTmp) throws IOException {
List<JavaFileObject> jfObjects = fileManager.getJavaFileObjectsFromFiles(sourceFiles);
boolean success = compile(jfObjects);
if (!success) {
return Collections.emptyList();
}
List<File> files = new ArrayList<>();
for (JavaClassObject classObject : fileManager.getClassLoader().getClassObjects()) {
Path path = outTmp.resolve(classObject.getName().replace('.', '/') + ".class");
FileUtils.makeDirsForFile(path);
Files.write(path, classObject.getBytes());
files.add(path.toFile());
}
return files;
}
public boolean compileNodes(List<ClassNode> clsNodeList) {
List<JavaFileObject> jfObjects = new ArrayList<>(clsNodeList.size());
for (ClassNode clsNode : clsNodeList) {
jfObjects.add(new StringJavaFileObject(clsNode.getFullName(), clsNode.getCode().getCodeStr()));
}
return compile(jfObjects);
}
private boolean compile(List<JavaFileObject> jfObjects) {
List<String> arguments = new ArrayList<>(); List<String> arguments = new ArrayList<>();
arguments.add(options.isIncludeDebugInfo() ? "-g" : "-g:none"); arguments.add(options.isIncludeDebugInfo() ? "-g" : "-g:none");
int javaVersion = options.getJavaVersion();
String javaVerStr = javaVersion <= 8 ? "1." + javaVersion : Integer.toString(javaVersion); String javaVerStr = javaVersion <= 8 ? "1." + javaVersion : Integer.toString(javaVersion);
arguments.add("-source"); arguments.add("-source");
arguments.add(javaVerStr); arguments.add(javaVerStr);
...@@ -55,57 +83,55 @@ public class StaticCompiler { ...@@ -55,57 +83,55 @@ public class StaticCompiler {
arguments.add(javaVerStr); arguments.add(javaVerStr);
arguments.addAll(options.getArguments()); arguments.addAll(options.getArguments());
DiagnosticListener<? super JavaFileObject> diag = new DiagnosticListener<JavaFileObject>() { CompilationTask compilerTask = compiler.getTask(null, fileManager, null, arguments, null, jfObjects);
@Override return Boolean.TRUE.equals(compilerTask.call());
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
System.out.println(diagnostic);
}
};
CompilationTask task = compiler.getTask(null, staticFileManager, diag, arguments, null, compilationUnits);
Boolean result = task.call();
fileManager.close();
if (Boolean.TRUE.equals(result)) {
return staticFileManager.outputFiles();
}
return Collections.emptyList();
} }
private static class StaticFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> { private ClassLoader getClassLoader() {
private final List<File> files = new ArrayList<>(); return fileManager.getClassLoader();
private final File outDir; }
protected StaticFileManager(StandardJavaFileManager fileManager, File outDir) { public Class<?> getClass(String clsFullName) throws ClassNotFoundException {
super(fileManager); return getClassLoader().loadClass(clsFullName);
this.outDir = outDir; }
}
@Override @NotNull
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) { public Method getMethod(Class<?> cls, String methodName, Class<?>[] types) throws NoSuchMethodException {
if (kind == JavaFileObject.Kind.CLASS) { return cls.getMethod(methodName, types);
File file = new File(outDir, className.replace('.', '/') + ".class"); }
files.add(file);
return new ClassFileObject(file, kind);
}
throw new UnsupportedOperationException("Can't save location with kind: " + kind);
}
public List<File> outputFiles() { public Object invoke(String clsFullName, String methodName, Class<?>[] types, Object[] args) {
return files; try {
for (Class<?> type : types) {
checkType(type);
}
Class<?> cls = getClass(clsFullName);
Method mth = getMethod(cls, methodName, types);
Object inst = cls.getConstructor().newInstance();
assertNotNull(mth, "Failed to get method " + methodName + '(' + Arrays.toString(types) + ')');
return mth.invoke(inst, args);
} catch (Throwable e) {
IntegrationTest.rethrow("Invoke error", e);
return null;
} }
} }
private static class ClassFileObject extends SimpleJavaFileObject { private Class<?> checkType(Class<?> type) throws ClassNotFoundException {
private final File file; if (type.isPrimitive()) {
return type;
protected ClassFileObject(File file, Kind kind) {
super(file.toURI(), kind);
this.file = file;
} }
if (type.isArray()) {
@Override return checkType(type.getComponentType());
public OutputStream openOutputStream() throws IOException { }
FileUtils.makeDirsForFile(file); Class<?> cls = getClassLoader().loadClass(type.getName());
return new FileOutputStream(file); if (type != cls) {
throw new IllegalArgumentException("Internal test class cannot be used in method invoke");
} }
return cls;
}
@Override
public void close() throws IOException {
fileManager.close();
} }
} }
...@@ -65,7 +65,7 @@ public class JavaConvertLoader { ...@@ -65,7 +65,7 @@ public class JavaConvertLoader {
} }
} }
result.addTempPath(jarFile); result.addTempPath(jarFile);
LOG.debug("Packed class files {} into jar {}", clsFiles.size(), jarFile); LOG.debug("Packed {} class files into jar: {}", clsFiles.size(), jarFile);
convertJar(result, jarFile); convertJar(result, jarFile);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error process class files", e); LOG.error("Error process class files", e);
...@@ -169,7 +169,7 @@ public class JavaConvertLoader { ...@@ -169,7 +169,7 @@ public class JavaConvertLoader {
} }
} }
List<Path> dexFiles = collectFilesInDir(tempDirectory); List<Path> dexFiles = collectFilesInDir(tempDirectory);
LOG.debug("Converted {} to dex files: {}", path.toAbsolutePath(), dexFiles.size()); LOG.debug("Converted {} to {} dex", path.toAbsolutePath(), dexFiles.size());
result.addConvertedFiles(dexFiles); result.addConvertedFiles(dexFiles);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册