refactor: use ICodeInfo interface instead CodeWriter

This commit is contained in:
Skylot
2019-12-21 06:43:52 +00:00
parent 1568008c67
commit c64ffde11f
16 changed files with 150 additions and 110 deletions
@@ -2,7 +2,12 @@ package jadx.api;
import java.util.Map;
import jadx.api.impl.SimpleCodeInfo;
public interface ICodeInfo {
ICodeInfo EMPTY = new SimpleCodeInfo("");
String getCodeStr();
Map<Integer, Integer> getLineMapping();
@@ -1,12 +1,11 @@
package jadx.api;
import jadx.core.codegen.CodeWriter;
import jadx.core.xmlgen.ResContainer;
public class ResourceFileContent extends ResourceFile {
private final CodeWriter content;
private final ICodeInfo content;
public ResourceFileContent(String name, ResourceType type, CodeWriter content) {
public ResourceFileContent(String name, ResourceType type, ICodeInfo content) {
super(null, name, type);
this.content = content;
}
@@ -16,6 +16,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile.ZipRef;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.codegen.CodeWriter;
import jadx.core.utils.Utils;
import jadx.core.utils.android.Res9patchStreamDecoder;
@@ -85,7 +86,7 @@ public final class ResourcesLoader {
CodeWriter cw = new CodeWriter();
cw.add("Error decode ").add(rf.getType().toString().toLowerCase());
Utils.appendStackTrace(cw, e.getCause());
return ResContainer.textResource(rf.getName(), cw);
return ResContainer.textResource(rf.getName(), cw.finish());
}
}
@@ -94,7 +95,7 @@ public final class ResourcesLoader {
switch (rf.getType()) {
case MANIFEST:
case XML:
CodeWriter content = jadxRef.getXmlParser().parse(inputStream);
ICodeInfo content = jadxRef.getXmlParser().parse(inputStream);
return ResContainer.textResource(rf.getName(), content);
case ARSC:
@@ -162,9 +163,9 @@ public final class ResourcesLoader {
}
}
public static CodeWriter loadToCodeWriter(InputStream is) throws IOException {
public static ICodeInfo loadToCodeWriter(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);
copyStream(is, baos);
return new CodeWriter(baos.toString("UTF-8"));
return new SimpleCodeInfo(baos.toString("UTF-8"));
}
}
@@ -0,0 +1,48 @@
package jadx.api.impl;
import java.util.Collections;
import java.util.Map;
import jadx.api.CodePosition;
import jadx.api.ICodeInfo;
public class SimpleCodeInfo implements ICodeInfo {
private final String code;
private final Map<Integer, Integer> lineMapping;
private final Map<CodePosition, Object> annotations;
public SimpleCodeInfo(String code) {
this(code, Collections.emptyMap(), Collections.emptyMap());
}
public SimpleCodeInfo(ICodeInfo codeInfo) {
this(codeInfo.getCodeStr(), codeInfo.getLineMapping(), codeInfo.getAnnotations());
}
public SimpleCodeInfo(String code, Map<Integer, Integer> lineMapping, Map<CodePosition, Object> annotations) {
this.code = code;
this.lineMapping = lineMapping;
this.annotations = annotations;
}
@Override
public String getCodeStr() {
return code;
}
@Override
public Map<Integer, Integer> getLineMapping() {
return lineMapping;
}
@Override
public Map<CodePosition, Object> getAnnotations() {
return annotations;
}
@Override
public String toString() {
return code;
}
}
@@ -12,6 +12,7 @@ import java.util.Set;
import com.android.dx.rop.code.AccessFlags;
import com.google.common.collect.Streams;
import jadx.api.ICodeInfo;
import jadx.api.JadxArgs;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
@@ -73,7 +74,7 @@ public class ClassGen {
return cls;
}
public CodeWriter makeClass() throws CodegenException {
public ICodeInfo makeClass() throws CodegenException {
CodeWriter clsBody = new CodeWriter();
addClassCode(clsBody);
@@ -4,6 +4,7 @@ import java.util.concurrent.Callable;
import jadx.api.ICodeInfo;
import jadx.api.JadxArgs;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.codegen.json.JsonCodeGen;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
@@ -13,7 +14,7 @@ public class CodeGen {
public static ICodeInfo generate(ClassNode cls) {
if (cls.contains(AFlag.DONT_GENERATE)) {
return CodeWriter.EMPTY;
return ICodeInfo.EMPTY;
}
JadxArgs args = cls.root().getArgs();
switch (args.getOutputFormat()) {
@@ -36,7 +37,7 @@ public class CodeGen {
private static ICodeInfo generateJson(ClassNode cls) {
JsonCodeGen codeGen = new JsonCodeGen(cls);
String clsJson = wrapCodeGen(cls, codeGen::process);
return new CodeWriter(clsJson);
return new SimpleCodeInfo(clsJson);
}
private static <R> R wrapCodeGen(ClassNode cls, Callable<R> codeGenFunc) {
@@ -1,7 +1,5 @@
package jadx.core.codegen;
import java.io.File;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -13,12 +11,11 @@ import org.slf4j.LoggerFactory;
import jadx.api.CodePosition;
import jadx.api.ICodeInfo;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.utils.StringUtils;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.ZipSecurity;
public class CodeWriter implements ICodeInfo {
public class CodeWriter {
private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class);
public static final String NL = System.getProperty("line.separator");
@@ -35,8 +32,6 @@ public class CodeWriter implements ICodeInfo {
INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
};
public static final CodeWriter EMPTY = new CodeWriter().finish();
private StringBuilder buf;
@Nullable
private String code;
@@ -58,12 +53,6 @@ public class CodeWriter implements ICodeInfo {
}
}
// create filled instance (just string wrapper)
public CodeWriter(String code) {
this.buf = null;
this.code = code;
}
public CodeWriter startLine() {
addLine();
addLineIndent();
@@ -244,11 +233,6 @@ public class CodeWriter implements ICodeInfo {
return annotations.put(pos, obj);
}
@Override
public Map<CodePosition, Object> getAnnotations() {
return annotations;
}
public void attachSourceLine(int sourceLine) {
if (sourceLine == 0) {
return;
@@ -263,27 +247,12 @@ public class CodeWriter implements ICodeInfo {
lineMap.put(decompiledLine, sourceLine);
}
@Override
public Map<Integer, Integer> getLineMapping() {
return lineMap;
}
public CodeWriter finish() {
public ICodeInfo finish() {
removeFirstEmptyLine();
buf.trimToSize();
processDefinitionAnnotations();
code = buf.toString();
buf = null;
annotations.entrySet().removeIf(entry -> {
Object v = entry.getValue();
if (v instanceof DefinitionWrapper) {
LineAttrNode l = ((DefinitionWrapper) v).getNode();
l.setDecompiledLine(entry.getKey().getLine());
return true;
}
return false;
});
return this;
return new SimpleCodeInfo(code, lineMap, annotations);
}
private void removeFirstEmptyLine() {
@@ -293,46 +262,33 @@ public class CodeWriter implements ICodeInfo {
}
}
private void processDefinitionAnnotations() {
if (!annotations.isEmpty()) {
annotations.entrySet().removeIf(entry -> {
Object v = entry.getValue();
if (v instanceof DefinitionWrapper) {
LineAttrNode l = ((DefinitionWrapper) v).getNode();
l.setDecompiledLine(entry.getKey().getLine());
return true;
}
return false;
});
}
}
public int bufLength() {
return buf.length();
}
@Override
public String getCodeStr() {
if (code == null) {
throw new NullPointerException("Code not set");
finish();
}
return code;
}
@Override
public String toString() {
return buf == null ? code : buf.toString();
}
public void save(File dir, String subDir, String fileName) {
if (!ZipSecurity.isValidZipEntryName(subDir) || !ZipSecurity.isValidZipEntryName(fileName)) {
return;
}
save(dir, new File(subDir, fileName).getPath());
}
public void save(File dir, String fileName) {
if (!ZipSecurity.isValidZipEntryName(fileName)) {
return;
}
save(new File(dir, fileName));
}
public void save(File file) {
if (code == null) {
finish();
}
File outFile = FileUtils.prepareFile(file);
try (PrintWriter out = new PrintWriter(outFile, "UTF-8")) {
out.println(code);
} catch (Exception e) {
LOG.error("Save file error", e);
}
return code != null ? code : buf.toString();
}
}
@@ -13,6 +13,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.CodePosition;
import jadx.api.ICodeInfo;
import jadx.api.JadxArgs;
import jadx.core.codegen.ClassGen;
import jadx.core.codegen.CodeWriter;
@@ -85,7 +86,7 @@ public class JsonCodeGen {
CodeGenUtils.addComments(cw, cls);
classGen.insertDecompilationProblems(cw, cls);
classGen.addClassDeclaration(cw);
jsonCls.setDeclaration(cw.finish().toString());
jsonCls.setDeclaration(cw.getCodeStr());
addFields(cls, jsonCls, classGen);
addMethods(cls, jsonCls, classGen);
@@ -128,7 +129,7 @@ public class JsonCodeGen {
CodeWriter cw = new CodeWriter();
classGen.addField(cw, field);
jsonField.setDeclaration(cw.finish().toString());
jsonField.setDeclaration(cw.getCodeStr());
jsonField.setAccessFlags(field.getAccessFlags().rawValue());
jsonCls.getFields().add(jsonField);
@@ -153,7 +154,7 @@ public class JsonCodeGen {
MethodGen mthGen = new MethodGen(classGen, mth);
CodeWriter cw = new CodeWriter();
mthGen.addDefinition(cw);
jsonMth.setDeclaration(cw.finish().toString());
jsonMth.setDeclaration(cw.getCodeStr());
jsonMth.setAccessFlags(mth.getAccessFlags().rawValue());
jsonMth.setLines(fillMthCode(mth, mthGen));
jsonMth.setOffset("0x" + Long.toHexString(mth.getMethodCodeOffset()));
@@ -166,14 +167,14 @@ public class JsonCodeGen {
return Collections.emptyList();
}
CodeWriter code = new CodeWriter();
CodeWriter cw = new CodeWriter();
try {
mthGen.addInstructions(code);
mthGen.addInstructions(cw);
} catch (Exception e) {
throw new JadxRuntimeException("Method generation error", e);
}
code.finish();
String codeStr = code.toString();
ICodeInfo code = cw.finish();
String codeStr = code.getCodeStr();
if (codeStr.isEmpty()) {
return Collections.emptyList();
}
@@ -280,7 +280,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
String clsRawName = topParentClass.getRawName();
if (searchInCache) {
ICodeInfo code = codeCache.get(clsRawName);
if (code != null) {
if (code != null && code != ICodeInfo.EMPTY) {
return code;
}
}
@@ -116,7 +116,11 @@ public class DotGraphVisitor extends AbstractVisitor {
+ (useRegions ? ".regions" : "")
+ (rawInsn ? ".raw" : "")
+ ".dot";
dot.save(dir, mth.getParentClass().getClassInfo().getAliasFullPath() + "_graphs", fileName);
File file = dir.toPath()
.resolve(mth.getParentClass().getClassInfo().getAliasFullPath() + "_graphs")
.resolve(fileName)
.toFile();
SaveCode.save(dot.finish(), file);
}
private void processMethodRegion(MethodNode mth) {
@@ -1,15 +1,21 @@
package jadx.core.dex.visitors;
import java.io.File;
import java.io.PrintWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo;
import jadx.api.JadxArgs;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.ZipSecurity;
public class SaveCode {
private static final Logger LOG = LoggerFactory.getLogger(SaveCode.class);
private SaveCode() {
}
@@ -21,18 +27,35 @@ public class SaveCode {
if (code == null) {
throw new JadxRuntimeException("Code not generated for class " + cls.getFullName());
}
if (code == CodeWriter.EMPTY) {
if (code == ICodeInfo.EMPTY) {
return;
}
CodeWriter clsCode;
if (code instanceof CodeWriter) {
clsCode = (CodeWriter) code;
} else {
// TODO: move 'save' method from CodeWriter
clsCode = new CodeWriter(code.getCodeStr());
String codeStr = code.getCodeStr();
if (codeStr.isEmpty()) {
return;
}
String fileName = cls.getClassInfo().getAliasFullPath() + getFileExtension(cls);
clsCode.save(dir, fileName);
save(codeStr, dir, fileName);
}
public static void save(String code, File dir, String fileName) {
if (!ZipSecurity.isValidZipEntryName(fileName)) {
return;
}
save(code, new File(dir, fileName));
}
public static void save(ICodeInfo codeInfo, File file) {
save(codeInfo.getCodeStr(), file);
}
public static void save(String code, File file) {
File outFile = FileUtils.prepareFile(file);
try (PrintWriter out = new PrintWriter(outFile, "UTF-8")) {
out.println(code);
} catch (Exception e) {
LOG.error("Save file error", e);
}
}
private static String getFileExtension(ClassNode cls) {
@@ -12,6 +12,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo;
import jadx.api.ResourcesLoader;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.info.ConstStorage;
@@ -80,7 +81,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
}
}
public synchronized CodeWriter parse(InputStream inputStream) throws IOException {
public synchronized ICodeInfo parse(InputStream inputStream) throws IOException {
is = new ParserStream(inputStream);
if (!isBinaryXml()) {
return ResourcesLoader.loadToCodeWriter(inputStream);
@@ -91,9 +92,8 @@ public class BinaryXMLParser extends CommonBinaryParser {
writer.add("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
firstElement = true;
decode();
writer.finish();
nsMap = null;
return writer;
return writer.finish();
}
private boolean isBinaryXml() throws IOException {
@@ -7,6 +7,7 @@ import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import jadx.api.ICodeInfo;
import jadx.api.ResourceFile;
import jadx.core.codegen.CodeWriter;
@@ -21,7 +22,7 @@ public class ResContainer implements Comparable<ResContainer> {
private final Object data;
private final List<ResContainer> subFiles;
public static ResContainer textResource(String name, CodeWriter content) {
public static ResContainer textResource(String name, ICodeInfo content) {
return new ResContainer(name, Collections.emptyList(), content, DataType.TEXT);
}
@@ -60,8 +61,8 @@ public class ResContainer implements Comparable<ResContainer> {
return dataType;
}
public CodeWriter getText() {
return (CodeWriter) data;
public ICodeInfo getText() {
return (ICodeInfo) data;
}
public byte[] getDecodedData() {
@@ -9,6 +9,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import jadx.api.ICodeInfo;
import jadx.core.codegen.CodeWriter;
import jadx.core.utils.StringUtils;
import jadx.core.xmlgen.entry.RawNamedValue;
@@ -57,8 +58,8 @@ public class ResXmlGen {
content.decIndent();
content.startLine("</resources>");
content.finish();
files.add(ResContainer.textResource(fileName, content));
ICodeInfo codeInfo = content.finish();
files.add(ResContainer.textResource(fileName, codeInfo));
}
Collections.sort(files);
return files;
@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile;
import jadx.api.ResourcesLoader;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
@@ -58,8 +58,7 @@ public class ResourcesSaver implements Runnable {
switch (rc.getDataType()) {
case TEXT:
case RES_TABLE:
CodeWriter cw = rc.getText();
cw.save(outFile);
SaveCode.save(rc.getText(), outFile);
return;
case DECODED_DATA:
@@ -16,7 +16,7 @@ import jadx.api.ResourceFile;
import jadx.api.ResourceFileContent;
import jadx.api.ResourceType;
import jadx.api.ResourcesLoader;
import jadx.core.codegen.CodeWriter;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.xmlgen.ResContainer;
import jadx.gui.utils.NLS;
import jadx.gui.utils.OverlayIcon;
@@ -146,17 +146,17 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
try {
return ResourcesLoader.decodeStream(rc.getResLink(), (size, is) -> {
if (size > 10 * 1024 * 1024L) {
return new CodeWriter("File too large for view");
return new SimpleCodeInfo("File too large for view");
}
return ResourcesLoader.loadToCodeWriter(is);
});
} catch (Exception e) {
return new CodeWriter("Failed to load resource file: \n" + jadx.core.utils.Utils.getStackTrace(e));
return new SimpleCodeInfo("Failed to load resource file: \n" + jadx.core.utils.Utils.getStackTrace(e));
}
case DECODED_DATA:
default:
return new CodeWriter("Unexpected resource type: " + rc);
return new SimpleCodeInfo("Unexpected resource type: " + rc);
}
}
@@ -164,8 +164,8 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
String resName = rc.getName();
String[] path = resName.split("/");
String resShortName = path.length == 0 ? resName : path[path.length - 1];
CodeWriter cw = rc.getText();
ResourceFileContent fileContent = new ResourceFileContent(resShortName, ResourceType.XML, cw);
ICodeInfo code = rc.getText();
ResourceFileContent fileContent = new ResourceFileContent(resShortName, ResourceType.XML, code);
addPath(path, root, new JResource(fileContent, resName, resShortName, JResType.FILE));
for (ResContainer subFile : rc.getSubFiles()) {