Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fcb120a3ed | |||
| 988628a2e7 | |||
| c24cdf5cc1 | |||
| d748e004d2 | |||
| 380b73d1b9 | |||
| ef85e29a9b | |||
| 1daf5d1090 | |||
| 9d2c0e4aea | |||
| 7277ebb9c4 | |||
| c18074f6aa | |||
| 8a706193e7 | |||
| 9d77f5f5df | |||
| 6951d0e646 | |||
| 73dd55eac2 | |||
| b5a9389cc6 | |||
| d905c96fbe | |||
| 03f03f85af | |||
| 2b00a8a406 | |||
| f31c2dcd21 | |||
| 7699cfac02 | |||
| 5c48a457b4 | |||
| b5f439e1aa | |||
| 202fe5a0a9 | |||
| 68ccf57bd4 | |||
| 84970759d8 | |||
| 53cac58ebe | |||
| adc32ed319 | |||
| 7f0815a7b2 | |||
| 68f5565b63 | |||
| c552fb857d | |||
| 8a4ec47b92 | |||
| d281126337 | |||
| 4fb6ada5ec | |||
| ab924faa1e | |||
| b12b129af7 | |||
| 017c6b4d42 | |||
| d55cd5fbb4 | |||
| 13a6b1c8c6 | |||
| 0bc37e5d32 | |||
| 46c8572887 | |||
| e6b919007c | |||
| ac5a6096bb | |||
| db527fbbda | |||
| 8f201f1fee | |||
| d1e0762c12 | |||
| 010ae99c69 | |||
| a4632d6e86 | |||
| 2a3162f869 | |||
| 2063fd0742 | |||
| 128fe8a839 | |||
| 2478fc3a1b | |||
| 5a68d3bef7 | |||
| 195eeceb62 | |||
| ec8309af49 | |||
| 627a4dc802 | |||
| e2018535ef | |||
| ee56610f06 | |||
| fb9ff7748a | |||
| cdfb46d9d3 | |||
| 5545a94a9e | |||
| 9e811d959b | |||
| 957d5394d2 | |||
| 95afe1219e | |||
| 07937f1d71 |
+1
-1
@@ -1,7 +1,7 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk7
|
||||
- openjdk7
|
||||
- openjdk6
|
||||
|
||||
before_install:
|
||||
|
||||
@@ -61,6 +61,20 @@ Example:
|
||||
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
||||
`DEFAULT_JVM_OPTS="-Xmx2500M"`
|
||||
|
||||
|
||||
### Contribution
|
||||
|
||||
To support this project you can:
|
||||
- Post thoughts about new features/optimizations that important to you
|
||||
- Submit bug using one of following patterns:
|
||||
* Java code examples which decompiles incorrectly
|
||||
* Error log and link to _public available_ apk file or app page on Google play
|
||||
|
||||
And any other comments will be very helpfull,
|
||||
because at current stage of development it is very time consuming
|
||||
to **find** new bugs, design and implement new features.
|
||||
Also I need to **prioritize** these task for complete most important at first.
|
||||
|
||||
---------------------------------------
|
||||
*Licensed under the Apache 2.0 License*
|
||||
|
||||
|
||||
+2
-1
@@ -32,13 +32,14 @@ subprojects {
|
||||
|
||||
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
testCompile 'junit:junit:4.11'
|
||||
testCompile 'org.mockito:mockito-core:1.9.5'
|
||||
testCompile 'org.mockito:mockito-core:1.10.10'
|
||||
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
|
||||
testCompile 'cglib:cglib-nodep:3.1'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
|
||||
@@ -29,9 +29,12 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||
protected int threadsCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)", help = true)
|
||||
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)")
|
||||
protected boolean fallbackMode = false;
|
||||
|
||||
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
|
||||
protected boolean showInconsistentCode = false;
|
||||
|
||||
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
||||
protected boolean cfgOutput = false;
|
||||
|
||||
@@ -181,6 +184,11 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
return fallbackMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return showInconsistentCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return verbose;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
||||
|
||||
dependencies {
|
||||
compile files('lib/dx-1.8.jar')
|
||||
compile 'org.ow2.asm:asm:5.0.3'
|
||||
runtime files(jadxClasspath)
|
||||
|
||||
compile files('lib/dx-1.10.jar')
|
||||
compile 'org.ow2.asm:asm:5.0.3'
|
||||
compile 'com.intellij:annotations:12.0'
|
||||
|
||||
testCompile 'org.smali:smali:2.0.3'
|
||||
}
|
||||
|
||||
task packTests(type: Jar) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -29,6 +29,11 @@ public class DefaultJadxArgs implements IJadxArgs {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return false;
|
||||
|
||||
@@ -13,5 +13,7 @@ public interface IJadxArgs {
|
||||
|
||||
boolean isFallbackMode();
|
||||
|
||||
boolean isShowInconsistentCode();
|
||||
|
||||
boolean isVerbose();
|
||||
}
|
||||
|
||||
@@ -116,28 +116,31 @@ public final class JavaClass implements JavaNode {
|
||||
public CodePosition getDefinitionPosition(int line, int offset) {
|
||||
Map<CodePosition, Object> map = getCodeAnnotations();
|
||||
Object obj = map.get(new CodePosition(line, offset));
|
||||
if (obj instanceof LineAttrNode) {
|
||||
ClassNode clsNode = null;
|
||||
if (obj instanceof ClassNode) {
|
||||
clsNode = (ClassNode) obj;
|
||||
} else if (obj instanceof MethodNode) {
|
||||
clsNode = ((MethodNode) obj).getParentClass();
|
||||
} else if (obj instanceof FieldNode) {
|
||||
clsNode = ((FieldNode) obj).getParentClass();
|
||||
}
|
||||
if (clsNode == null) {
|
||||
return null;
|
||||
}
|
||||
clsNode = clsNode.getParentClass();
|
||||
JavaClass jCls = decompiler.findJavaClass(clsNode);
|
||||
if (jCls == null) {
|
||||
return null;
|
||||
}
|
||||
jCls.decompile();
|
||||
int defLine = ((LineAttrNode) obj).getDecompiledLine();
|
||||
return new CodePosition(jCls, defLine, 0);
|
||||
if (!(obj instanceof LineAttrNode)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
ClassNode clsNode = null;
|
||||
if (obj instanceof ClassNode) {
|
||||
clsNode = (ClassNode) obj;
|
||||
} else if (obj instanceof MethodNode) {
|
||||
clsNode = ((MethodNode) obj).getParentClass();
|
||||
} else if (obj instanceof FieldNode) {
|
||||
clsNode = ((FieldNode) obj).getParentClass();
|
||||
}
|
||||
if (clsNode == null) {
|
||||
return null;
|
||||
}
|
||||
clsNode = clsNode.getTopParentClass();
|
||||
JavaClass jCls = decompiler.findJavaClass(clsNode);
|
||||
if (jCls == null) {
|
||||
return null;
|
||||
}
|
||||
jCls.decompile();
|
||||
int defLine = ((LineAttrNode) obj).getDecompiledLine();
|
||||
if (defLine == 0) {
|
||||
return null;
|
||||
}
|
||||
return new CodePosition(jCls, defLine, 0);
|
||||
}
|
||||
|
||||
public Integer getSourceLine(int decompiledLine) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import jadx.core.dex.visitors.ReSugarCode;
|
||||
import jadx.core.dex.visitors.SimplifyVisitor;
|
||||
import jadx.core.dex.visitors.regions.CheckRegions;
|
||||
import jadx.core.dex.visitors.regions.IfRegionVisitor;
|
||||
import jadx.core.dex.visitors.regions.LoopRegionVisitor;
|
||||
import jadx.core.dex.visitors.regions.ProcessVariables;
|
||||
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
|
||||
import jadx.core.dex.visitors.regions.ReturnVisitor;
|
||||
@@ -59,7 +60,7 @@ public class Jadx {
|
||||
passes.add(new DebugInfoVisitor());
|
||||
passes.add(new TypeInference());
|
||||
if (args.isRawCFGOutput()) {
|
||||
passes.add(new DotGraphVisitor(outDir, false, true));
|
||||
passes.add(DotGraphVisitor.dumpRaw(outDir));
|
||||
}
|
||||
|
||||
passes.add(new ConstInlinerVisitor());
|
||||
@@ -72,7 +73,7 @@ public class Jadx {
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new ReSugarCode());
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(new DotGraphVisitor(outDir, false));
|
||||
passes.add(DotGraphVisitor.dump(outDir));
|
||||
}
|
||||
|
||||
passes.add(new RegionMakerVisitor());
|
||||
@@ -81,16 +82,17 @@ public class Jadx {
|
||||
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new SimplifyVisitor());
|
||||
passes.add(new ProcessVariables());
|
||||
passes.add(new CheckRegions());
|
||||
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(new DotGraphVisitor(outDir, true));
|
||||
passes.add(DotGraphVisitor.dumpRegions(outDir));
|
||||
}
|
||||
|
||||
passes.add(new MethodInlineVisitor());
|
||||
passes.add(new ClassModifier());
|
||||
passes.add(new PrepareForCodeGen());
|
||||
passes.add(new LoopRegionVisitor());
|
||||
passes.add(new ProcessVariables());
|
||||
}
|
||||
passes.add(new CodeGen(args));
|
||||
return passes;
|
||||
|
||||
@@ -22,7 +22,7 @@ public final class ProcessClass {
|
||||
DepthTraversal.visit(visitor, cls);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Class process exception: " + cls, e);
|
||||
LOG.error("Class process exception: {}", cls, e);
|
||||
} finally {
|
||||
cls.unload();
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package jadx.core.clsp;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
@@ -96,13 +96,13 @@ public class ClsSet {
|
||||
private static NClass getCls(String fullName, Map<String, NClass> names) {
|
||||
NClass id = names.get(fullName);
|
||||
if (id == null && !names.containsKey(fullName)) {
|
||||
LOG.warn("Class not found: " + fullName);
|
||||
LOG.warn("Class not found: {}", fullName);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void save(File output) throws IOException {
|
||||
Utils.makeDirsForFile(output);
|
||||
FileUtils.makeDirsForFile(output);
|
||||
|
||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
||||
try {
|
||||
@@ -132,7 +132,7 @@ public class ClsSet {
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
|
||||
LOG.info("Classes count: " + classes.length);
|
||||
LOG.info("Classes count: {}", classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (NClass cls : classes) {
|
||||
writeString(out, cls.getName());
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ConvertToClsSet {
|
||||
}
|
||||
}
|
||||
for (InputFile inputFile : inputFiles) {
|
||||
LOG.info("Loaded: " + inputFile.getFile());
|
||||
LOG.info("Loaded: {}", inputFile.getFile());
|
||||
}
|
||||
|
||||
RootNode root = new RootNode();
|
||||
@@ -48,7 +48,7 @@ public class ConvertToClsSet {
|
||||
ClsSet set = new ClsSet();
|
||||
set.load(root);
|
||||
set.save(output);
|
||||
LOG.info("Output: " + output);
|
||||
LOG.info("Output: {}", output);
|
||||
LOG.info("done");
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class AnnotationGen {
|
||||
|
||||
public void addForParameter(CodeWriter code, MethodParameters paramsAnnotations, int n) {
|
||||
AnnotationsList aList = paramsAnnotations.getParamList().get(n);
|
||||
if (aList == null || aList.size() == 0) {
|
||||
if (aList == null || aList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Annotation a : aList.getAll()) {
|
||||
@@ -54,7 +54,7 @@ public class AnnotationGen {
|
||||
|
||||
private void add(IAttributeNode node, CodeWriter code) {
|
||||
AnnotationsList aList = node.get(AType.ANNOTATION_LIST);
|
||||
if (aList == null || aList.size() == 0) {
|
||||
if (aList == null || aList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Annotation a : aList.getAll()) {
|
||||
@@ -150,9 +150,9 @@ public class AnnotationGen {
|
||||
// must be a static field
|
||||
FieldInfo field = (FieldInfo) val;
|
||||
InsnGen.makeStaticFieldAccess(code, field, classGen);
|
||||
} else if (val instanceof List) {
|
||||
} else if (val instanceof Iterable) {
|
||||
code.add('{');
|
||||
Iterator<?> it = ((List) val).iterator();
|
||||
Iterator<?> it = ((Iterable) val).iterator();
|
||||
while (it.hasNext()) {
|
||||
Object obj = it.next();
|
||||
encodeValue(code, obj);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
@@ -23,6 +24,7 @@ import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -38,14 +40,28 @@ import com.android.dx.rop.code.AccessFlags;
|
||||
public class ClassGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
|
||||
|
||||
public static final Comparator<MethodNode> METHOD_LINE_COMPARATOR = new Comparator<MethodNode>() {
|
||||
@Override
|
||||
public int compare(MethodNode a, MethodNode b) {
|
||||
return Utils.compare(a.getSourceLine(), b.getSourceLine());
|
||||
}
|
||||
};
|
||||
|
||||
private final ClassNode cls;
|
||||
private final ClassGen parentGen;
|
||||
private final AnnotationGen annotationGen;
|
||||
private final boolean fallback;
|
||||
|
||||
private boolean showInconsistentCode = false;
|
||||
|
||||
private final Set<ClassInfo> imports = new HashSet<ClassInfo>();
|
||||
private int clsDeclLine;
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen, IJadxArgs jadxArgs) {
|
||||
this(cls, parentClsGen, jadxArgs.isFallbackMode());
|
||||
this.showInconsistentCode = jadxArgs.isShowInconsistentCode();
|
||||
}
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen, boolean fallback) {
|
||||
this.cls = cls;
|
||||
this.parentGen = parentClsGen;
|
||||
@@ -108,6 +124,11 @@ public class ClassGen {
|
||||
.remove(AccessFlags.ACC_STATIC);
|
||||
}
|
||||
|
||||
// 'static' modifier not allowed for top classes (not inner)
|
||||
if (!cls.getClassInfo().isInner()) {
|
||||
af = af.remove(AccessFlags.ACC_STATIC);
|
||||
}
|
||||
|
||||
annotationGen.addForClass(clsCode);
|
||||
insertSourceFileInfo(clsCode, cls);
|
||||
clsCode.startLine(af.makeString());
|
||||
@@ -135,7 +156,7 @@ public class ClassGen {
|
||||
clsCode.add(' ');
|
||||
}
|
||||
|
||||
if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) {
|
||||
if (!cls.getInterfaces().isEmpty() && !af.isAnnotation()) {
|
||||
if (cls.getAccessFlags().isInterface()) {
|
||||
clsCode.add("extends ");
|
||||
} else {
|
||||
@@ -226,7 +247,8 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
private void addMethods(CodeWriter code) {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
List<MethodNode> methods = sortMethodsByLine(cls.getMethods());
|
||||
for (MethodNode mth : methods) {
|
||||
if (mth.contains(AFlag.DONT_GENERATE)) {
|
||||
continue;
|
||||
}
|
||||
@@ -242,6 +264,12 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MethodNode> sortMethodsByLine(List<MethodNode> methods) {
|
||||
List<MethodNode> out = new ArrayList<MethodNode>(methods);
|
||||
Collections.sort(out, METHOD_LINE_COMPARATOR);
|
||||
return out;
|
||||
}
|
||||
|
||||
private boolean isMethodsPresents() {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
if (!mth.contains(AFlag.DONT_GENERATE)) {
|
||||
@@ -269,6 +297,9 @@ public class ClassGen {
|
||||
code.startLine("/* JADX WARNING: inconsistent code. */");
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
ErrorsCounter.methodError(mth, "Inconsistent code");
|
||||
if (showInconsistentCode) {
|
||||
mth.remove(AFlag.INCONSISTENT_CODE);
|
||||
}
|
||||
}
|
||||
MethodGen mthGen;
|
||||
if (badCode || mth.contains(AType.JADX_ERROR)) {
|
||||
@@ -331,7 +362,7 @@ public class ClassGen {
|
||||
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
|
||||
EnumField f = it.next();
|
||||
code.startLine(f.getName());
|
||||
if (f.getArgs().size() != 0) {
|
||||
if (!f.getArgs().isEmpty()) {
|
||||
code.add('(');
|
||||
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
|
||||
InsnArg arg = aIt.next();
|
||||
@@ -387,8 +418,8 @@ public class ClassGen {
|
||||
code.attachAnnotation(classNode);
|
||||
}
|
||||
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
|
||||
ArgType[] generics = classInfo.getType().getGenericTypes();
|
||||
code.add(baseClass);
|
||||
ArgType[] generics = classInfo.getType().getGenericTypes();
|
||||
if (generics != null) {
|
||||
code.add('<');
|
||||
int len = generics.length;
|
||||
@@ -435,7 +466,7 @@ public class ClassGen {
|
||||
if (classNode != null && !classNode.getAccessFlags().isPublic()) {
|
||||
return shortName;
|
||||
}
|
||||
if (searchCollision(cls.dex(), useCls, shortName)) {
|
||||
if (searchCollision(cls.dex(), useCls, classInfo)) {
|
||||
return fullName;
|
||||
}
|
||||
if (classInfo.getPackage().equals(useCls.getPackage())) {
|
||||
@@ -481,22 +512,24 @@ public class ClassGen {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean searchCollision(DexNode dex, ClassInfo useCls, String shortName) {
|
||||
private static boolean searchCollision(DexNode dex, ClassInfo useCls, ClassInfo searchCls) {
|
||||
if (useCls == null) {
|
||||
return false;
|
||||
}
|
||||
String shortName = searchCls.getShortName();
|
||||
if (useCls.getShortName().equals(shortName)) {
|
||||
return true;
|
||||
}
|
||||
ClassNode classNode = dex.resolveClass(useCls);
|
||||
if (classNode != null) {
|
||||
for (ClassNode inner : classNode.getInnerClasses()) {
|
||||
if (inner.getShortName().equals(shortName)) {
|
||||
if (inner.getShortName().equals(shortName)
|
||||
&& !inner.getClassInfo().equals(searchCls)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchCollision(dex, useCls.getParentClass(), shortName);
|
||||
return searchCollision(dex, useCls.getParentClass(), searchCls);
|
||||
}
|
||||
|
||||
private void insertSourceFileInfo(CodeWriter code, AttrNode node) {
|
||||
|
||||
@@ -15,15 +15,11 @@ public class CodeGen extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws CodegenException {
|
||||
ClassGen clsGen = new ClassGen(cls, null, isFallbackMode());
|
||||
ClassGen clsGen = new ClassGen(cls, null, args);
|
||||
CodeWriter clsCode = clsGen.makeClass();
|
||||
clsCode.finish();
|
||||
cls.setCode(clsCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFallbackMode() {
|
||||
return args.isFallbackMode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.core.codegen;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
@@ -22,6 +22,8 @@ public class CodeWriter {
|
||||
public static final String NL = System.getProperty("line.separator");
|
||||
public static final String INDENT = " ";
|
||||
|
||||
private static final boolean ADD_LINE_NUMBERS = false;
|
||||
|
||||
private static final String[] INDENT_CACHE = {
|
||||
"",
|
||||
INDENT,
|
||||
@@ -43,6 +45,9 @@ public class CodeWriter {
|
||||
public CodeWriter() {
|
||||
this.indent = 0;
|
||||
this.indentStr = "";
|
||||
if (ADD_LINE_NUMBERS) {
|
||||
incIndent(2);
|
||||
}
|
||||
}
|
||||
|
||||
public CodeWriter startLine() {
|
||||
@@ -65,6 +70,26 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter startLineWithNum(int sourceLine) {
|
||||
if (sourceLine == 0) {
|
||||
startLine();
|
||||
return this;
|
||||
}
|
||||
if (ADD_LINE_NUMBERS) {
|
||||
newLine();
|
||||
attachSourceLine(sourceLine);
|
||||
String ln = "/* " + sourceLine + " */ ";
|
||||
add(ln);
|
||||
if (indentStr.length() > ln.length()) {
|
||||
add(indentStr.substring(ln.length()));
|
||||
}
|
||||
} else {
|
||||
startLine();
|
||||
attachSourceLine(sourceLine);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(String str) {
|
||||
buf.append(str);
|
||||
offset += str.length();
|
||||
@@ -263,11 +288,11 @@ public class CodeWriter {
|
||||
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
Utils.makeDirsForFile(file);
|
||||
FileUtils.makeDirsForFile(file);
|
||||
out = new PrintWriter(file, "UTF-8");
|
||||
String code = buf.toString();
|
||||
code = removeFirstEmptyLine(code);
|
||||
out.print(code);
|
||||
out.println(code);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Save file error", e);
|
||||
} finally {
|
||||
|
||||
@@ -8,14 +8,16 @@ import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.Compare;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.IfCondition.Mode;
|
||||
import jadx.core.dex.regions.conditions.Compare;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.dex.regions.conditions.IfCondition.Mode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -23,42 +25,83 @@ import org.slf4j.LoggerFactory;
|
||||
public class ConditionGen extends InsnGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ConditionGen.class);
|
||||
|
||||
private static class CondStack {
|
||||
private final Queue<IfCondition> stack = new LinkedList<IfCondition>();
|
||||
|
||||
public Queue<IfCondition> getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
public void push(IfCondition cond) {
|
||||
stack.add(cond);
|
||||
}
|
||||
|
||||
public IfCondition pop() {
|
||||
return stack.poll();
|
||||
}
|
||||
}
|
||||
|
||||
public ConditionGen(InsnGen insnGen) {
|
||||
super(insnGen.mgen, insnGen.fallback);
|
||||
}
|
||||
|
||||
void add(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
add(code, new CondStack(), condition);
|
||||
}
|
||||
|
||||
void wrap(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
wrap(code, new CondStack(), condition);
|
||||
}
|
||||
|
||||
private void add(CodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {
|
||||
stack.push(condition);
|
||||
switch (condition.getMode()) {
|
||||
case COMPARE:
|
||||
addCompare(code, condition.getCompare());
|
||||
addCompare(code, stack, condition.getCompare());
|
||||
break;
|
||||
|
||||
case TERNARY:
|
||||
addTernary(code, stack, condition);
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
addNot(code, condition);
|
||||
addNot(code, stack, condition);
|
||||
break;
|
||||
|
||||
case AND:
|
||||
case OR:
|
||||
addAndOr(code, condition);
|
||||
addAndOr(code, stack, condition);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new JadxRuntimeException("Unknown condition mode: " + condition);
|
||||
throw new JadxRuntimeException("Unknown condition mode: " + condition.getMode());
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
void wrap(CodeWriter code, IfCondition cond) throws CodegenException {
|
||||
private void wrap(CodeWriter code, CondStack stack, IfCondition cond) throws CodegenException {
|
||||
boolean wrap = isWrapNeeded(cond);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
add(code, cond);
|
||||
add(code, stack, cond);
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
}
|
||||
|
||||
private void addCompare(CodeWriter code, Compare compare) throws CodegenException {
|
||||
private void wrap(CodeWriter code, InsnArg firstArg) throws CodegenException {
|
||||
boolean wrap = isArgWrapNeeded(firstArg);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
addArg(code, firstArg, false);
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
}
|
||||
|
||||
private void addCompare(CodeWriter code, CondStack stack, Compare compare) throws CodegenException {
|
||||
IfOp op = compare.getOp();
|
||||
InsnArg firstArg = compare.getA();
|
||||
InsnArg secondArg = compare.getB();
|
||||
@@ -71,19 +114,16 @@ public class ConditionGen extends InsnGen {
|
||||
}
|
||||
if (op == IfOp.EQ) {
|
||||
// == true
|
||||
addArg(code, firstArg, false);
|
||||
if (stack.getStack().size() == 1) {
|
||||
addArg(code, firstArg, false);
|
||||
} else {
|
||||
wrap(code, firstArg);
|
||||
}
|
||||
return;
|
||||
} else if (op == IfOp.NE) {
|
||||
// != true
|
||||
code.add('!');
|
||||
boolean wrap = isArgWrapNeeded(firstArg);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
addArg(code, firstArg, false);
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
wrap(code, firstArg);
|
||||
return;
|
||||
}
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
|
||||
@@ -94,16 +134,24 @@ public class ConditionGen extends InsnGen {
|
||||
addArg(code, secondArg, isArgWrapNeeded(secondArg));
|
||||
}
|
||||
|
||||
private void addNot(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
code.add('!');
|
||||
wrap(code, condition.getArgs().get(0));
|
||||
private void addTernary(CodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {
|
||||
add(code, stack, condition.first());
|
||||
code.add(" ? ");
|
||||
add(code, stack, condition.second());
|
||||
code.add(" : ");
|
||||
add(code, stack, condition.third());
|
||||
}
|
||||
|
||||
private void addAndOr(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
private void addNot(CodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {
|
||||
code.add('!');
|
||||
wrap(code, stack, condition.getArgs().get(0));
|
||||
}
|
||||
|
||||
private void addAndOr(CodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {
|
||||
String mode = condition.getMode() == Mode.AND ? " && " : " || ";
|
||||
Iterator<IfCondition> it = condition.getArgs().iterator();
|
||||
while (it.hasNext()) {
|
||||
wrap(code, it.next());
|
||||
wrap(code, stack, it.next());
|
||||
if (it.hasNext()) {
|
||||
code.add(mode);
|
||||
}
|
||||
@@ -111,7 +159,13 @@ public class ConditionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private boolean isWrapNeeded(IfCondition condition) {
|
||||
return !condition.isCompare() && condition.getMode() != Mode.NOT;
|
||||
if (condition.isCompare()) {
|
||||
return false;
|
||||
}
|
||||
if (condition.getMode() != Mode.NOT) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isArgWrapNeeded(InsnArg arg) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.core.codegen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
@@ -24,7 +25,7 @@ import jadx.core.dex.instructions.args.FieldArg;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.Named;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
@@ -46,7 +47,9 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -58,9 +61,10 @@ public class InsnGen {
|
||||
protected final RootNode root;
|
||||
protected final boolean fallback;
|
||||
|
||||
private enum Flags {
|
||||
protected enum Flags {
|
||||
BODY_ONLY,
|
||||
BODY_ONLY_NOWRAP,
|
||||
INLINE
|
||||
}
|
||||
|
||||
public InsnGen(MethodGen mgen, boolean fallback) {
|
||||
@@ -95,7 +99,7 @@ public class InsnGen {
|
||||
Flags flag = wrap ? Flags.BODY_ONLY : Flags.BODY_ONLY_NOWRAP;
|
||||
makeInsn(((InsnWrapArg) arg).getWrapInsn(), code, flag);
|
||||
} else if (arg.isNamed()) {
|
||||
code.add(((NamedArg) arg).getName());
|
||||
code.add(((Named) arg).getName());
|
||||
} else if (arg.isField()) {
|
||||
FieldArg f = (FieldArg) arg;
|
||||
if (f.isStatic()) {
|
||||
@@ -128,7 +132,14 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
|
||||
FieldNode fieldNode = mth.getParentClass().searchField(field);
|
||||
ClassNode pCls = mth.getParentClass();
|
||||
FieldNode fieldNode = pCls.searchField(field);
|
||||
while (fieldNode == null
|
||||
&& pCls.getParentClass() != pCls
|
||||
&& pCls.getParentClass() != null) {
|
||||
pCls = pCls.getParentClass();
|
||||
fieldNode = pCls.searchField(field);
|
||||
}
|
||||
if (fieldNode != null) {
|
||||
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
||||
if (replace != null) {
|
||||
@@ -178,7 +189,7 @@ public class InsnGen {
|
||||
mgen.getClassGen().useClass(code, cls);
|
||||
}
|
||||
|
||||
private void useType(CodeWriter code, ArgType type) {
|
||||
protected void useType(CodeWriter code, ArgType type) {
|
||||
mgen.getClassGen().useType(code, type);
|
||||
}
|
||||
|
||||
@@ -186,26 +197,27 @@ public class InsnGen {
|
||||
return makeInsn(insn, code, null);
|
||||
}
|
||||
|
||||
private boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException {
|
||||
protected boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException {
|
||||
try {
|
||||
if (insn.getType() == InsnType.NOP) {
|
||||
return false;
|
||||
}
|
||||
EnumSet<Flags> state = EnumSet.noneOf(Flags.class);
|
||||
Set<Flags> state = EnumSet.noneOf(Flags.class);
|
||||
if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {
|
||||
state.add(flag);
|
||||
makeInsnBody(code, insn, state);
|
||||
} else {
|
||||
code.startLine();
|
||||
if (insn.getSourceLine() != 0) {
|
||||
code.attachSourceLine(insn.getSourceLine());
|
||||
if (flag != Flags.INLINE) {
|
||||
code.startLineWithNum(insn.getSourceLine());
|
||||
}
|
||||
if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) {
|
||||
if (insn.getResult() != null && !insn.contains(AFlag.ARITH_ONEARG)) {
|
||||
assignVar(code, insn);
|
||||
code.add(" = ");
|
||||
}
|
||||
makeInsnBody(code, insn, state);
|
||||
code.add(';');
|
||||
if (flag != Flags.INLINE) {
|
||||
code.add(';');
|
||||
}
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
throw new CodegenException(mth, "Error generate insn: " + insn, th);
|
||||
@@ -213,7 +225,7 @@ public class InsnGen {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void makeInsnBody(CodeWriter code, InsnNode insn, EnumSet<Flags> state) throws CodegenException {
|
||||
private void makeInsnBody(CodeWriter code, InsnNode insn, Set<Flags> state) throws CodegenException {
|
||||
switch (insn.getType()) {
|
||||
case CONST_STR:
|
||||
String str = ((ConstStringNode) insn).getString();
|
||||
@@ -255,10 +267,6 @@ public class InsnGen {
|
||||
makeArith((ArithNode) insn, code, state);
|
||||
break;
|
||||
|
||||
case ARITH_ONEARG:
|
||||
makeArithOneArg((ArithNode) insn, code);
|
||||
break;
|
||||
|
||||
case NEG: {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
@@ -283,6 +291,10 @@ public class InsnGen {
|
||||
|
||||
case BREAK:
|
||||
code.add("break");
|
||||
LoopLabelAttr labelAttr = insn.get(AType.LOOP_LABEL);
|
||||
if (labelAttr != null) {
|
||||
code.add(' ').add(mgen.getNameGen().getLoopLabel(labelAttr));
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTINUE:
|
||||
@@ -304,7 +316,7 @@ public class InsnGen {
|
||||
addArg(code, insn.getArg(0));
|
||||
code.add(" == ");
|
||||
addArg(code, insn.getArg(1));
|
||||
code.add("? 0 : -1))");
|
||||
code.add(" ? 0 : -1))");
|
||||
break;
|
||||
|
||||
case INSTANCE_OF: {
|
||||
@@ -425,19 +437,11 @@ public class InsnGen {
|
||||
}
|
||||
break;
|
||||
|
||||
case MOVE_EXCEPTION:
|
||||
if (isFallback()) {
|
||||
code.add("move-exception");
|
||||
} else {
|
||||
addArg(code, insn.getArg(0));
|
||||
}
|
||||
break;
|
||||
|
||||
case TERNARY:
|
||||
makeTernary((TernaryInsn) insn, code, state);
|
||||
break;
|
||||
|
||||
case ARGS:
|
||||
case ONE_ARG:
|
||||
addArg(code, insn.getArg(0));
|
||||
break;
|
||||
|
||||
@@ -461,6 +465,11 @@ public class InsnGen {
|
||||
code.add("goto ").add(MethodGen.getLabelName(((GotoNode) insn).getTarget()));
|
||||
break;
|
||||
|
||||
case MOVE_EXCEPTION:
|
||||
assert isFallback();
|
||||
code.add("move-exception");
|
||||
break;
|
||||
|
||||
case SWITCH:
|
||||
assert isFallback();
|
||||
SwitchNode sw = (SwitchNode) insn;
|
||||
@@ -505,6 +514,13 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
private void fillArray(FillArrayNode insn, CodeWriter code) throws CodegenException {
|
||||
String filledArray = makeArrayElements(insn);
|
||||
code.add("new ");
|
||||
useType(code, insn.getElementType());
|
||||
code.add("[]{").add(filledArray).add('}');
|
||||
}
|
||||
|
||||
private String makeArrayElements(FillArrayNode insn) throws CodegenException {
|
||||
ArgType insnArrayType = insn.getResult().getType();
|
||||
ArgType insnElementType = insnArrayType.getArrayElement();
|
||||
ArgType elType = insn.getElementType();
|
||||
@@ -513,10 +529,13 @@ public class InsnGen {
|
||||
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
||||
+ ", element type: " + elType + ", insn element type: " + insnElementType
|
||||
);
|
||||
if (!elType.isTypeKnown()) {
|
||||
elType = insnElementType.isTypeKnown() ? insnElementType : elType.selectFirst();
|
||||
}
|
||||
}
|
||||
if (!elType.isTypeKnown()) {
|
||||
LOG.warn("Unknown array element type: {} in mth: {}", elType, mth);
|
||||
elType = insnElementType.isTypeKnown() ? insnElementType : elType.selectFirst();
|
||||
}
|
||||
insn.mergeElementType(elType);
|
||||
|
||||
StringBuilder str = new StringBuilder();
|
||||
Object data = insn.getData();
|
||||
switch (elType.getPrimitiveType()) {
|
||||
@@ -558,9 +577,7 @@ public class InsnGen {
|
||||
}
|
||||
int len = str.length();
|
||||
str.delete(len - 2, len);
|
||||
code.add("new ");
|
||||
useType(code, elType);
|
||||
code.add("[]{").add(str.toString()).add('}');
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
||||
@@ -604,7 +621,7 @@ public class InsnGen {
|
||||
code.add("new ");
|
||||
useClass(code, insn.getClassType());
|
||||
}
|
||||
generateArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth()));
|
||||
generateMethodArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth()));
|
||||
}
|
||||
|
||||
private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
@@ -649,47 +666,74 @@ public class InsnGen {
|
||||
code.attachAnnotation(callMthNode);
|
||||
}
|
||||
code.add(callMth.getName());
|
||||
generateArguments(code, insn, k, callMthNode);
|
||||
generateMethodArguments(code, insn, k, callMthNode);
|
||||
}
|
||||
|
||||
private void generateArguments(CodeWriter code, InsnNode insn, int k, MethodNode callMth) throws CodegenException {
|
||||
private void generateMethodArguments(CodeWriter code, InsnNode insn, int startArgNum,
|
||||
@Nullable MethodNode callMth) throws CodegenException {
|
||||
int k = startArgNum;
|
||||
if (callMth != null && callMth.contains(AFlag.SKIP_FIRST_ARG)) {
|
||||
k++;
|
||||
}
|
||||
int argsCount = insn.getArgsCount();
|
||||
if (callMth != null && callMth.isArgsOverload()) {
|
||||
// add additional argument casts for overloaded methods
|
||||
List<ArgType> originalType = callMth.getMethodInfo().getArgumentsTypes();
|
||||
int origPos = 0;
|
||||
code.add('(');
|
||||
code.add('(');
|
||||
if (k < argsCount) {
|
||||
boolean overloaded = callMth != null && callMth.isArgsOverload();
|
||||
for (int i = k; i < argsCount; i++) {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
ArgType origType = originalType.get(origPos);
|
||||
if (!arg.getType().equals(origType)) {
|
||||
code.add('(');
|
||||
useType(code, origType);
|
||||
code.add(')');
|
||||
addArg(code, arg, true);
|
||||
} else {
|
||||
addArg(code, arg, false);
|
||||
boolean cast = overloaded && processOverloadedArg(code, callMth, arg, i - startArgNum);
|
||||
if (!cast && i == argsCount - 1 && processVarArg(code, callMth, arg)) {
|
||||
continue;
|
||||
}
|
||||
addArg(code, arg, false);
|
||||
if (i < argsCount - 1) {
|
||||
code.add(", ");
|
||||
}
|
||||
origPos++;
|
||||
}
|
||||
code.add(')');
|
||||
} else {
|
||||
}
|
||||
code.add(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional cast for overloaded method argument.
|
||||
*/
|
||||
private boolean processOverloadedArg(CodeWriter code, MethodNode callMth, InsnArg arg, int origPos) {
|
||||
ArgType origType = callMth.getMethodInfo().getArgumentsTypes().get(origPos);
|
||||
if (!arg.getType().equals(origType)) {
|
||||
code.add('(');
|
||||
if (k < argsCount) {
|
||||
addArg(code, insn.getArg(k), false);
|
||||
for (int i = k + 1; i < argsCount; i++) {
|
||||
useType(code, origType);
|
||||
code.add(") ");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand varArgs from filled array.
|
||||
*/
|
||||
private boolean processVarArg(CodeWriter code, MethodNode callMth, InsnArg lastArg) throws CodegenException {
|
||||
if (callMth == null || !callMth.getAccessFlags().isVarArgs()) {
|
||||
return false;
|
||||
}
|
||||
if (!lastArg.getType().isArray() || !lastArg.isInsnWrap()) {
|
||||
return false;
|
||||
}
|
||||
InsnNode insn = ((InsnWrapArg) lastArg).getWrapInsn();
|
||||
if (insn.getType() == InsnType.FILLED_NEW_ARRAY) {
|
||||
int count = insn.getArgsCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
InsnArg elemArg = insn.getArg(i);
|
||||
addArg(code, elemArg, false);
|
||||
if (i < count - 1) {
|
||||
code.add(", ");
|
||||
addArg(code, insn.getArg(i), false);
|
||||
}
|
||||
}
|
||||
code.add(')');
|
||||
return true;
|
||||
} else if (insn.getType() == InsnType.FILL_ARRAY) {
|
||||
code.add(makeArrayElements((FillArrayNode) insn));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
@@ -736,7 +780,7 @@ public class InsnGen {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void makeTernary(TernaryInsn insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
|
||||
private void makeTernary(TernaryInsn insn, CodeWriter code, Set<Flags> state) throws CodegenException {
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
@@ -758,7 +802,11 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
|
||||
private void makeArith(ArithNode insn, CodeWriter code, Set<Flags> state) throws CodegenException {
|
||||
if (insn.contains(AFlag.ARITH_ONEARG)) {
|
||||
makeArithOneArg(insn, code);
|
||||
return;
|
||||
}
|
||||
// wrap insn in brackets for save correct operation order
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY) && !insn.contains(AFlag.DONT_WRAP);
|
||||
if (wrap) {
|
||||
@@ -776,7 +824,7 @@ public class InsnGen {
|
||||
|
||||
private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException {
|
||||
ArithOp op = insn.getOp();
|
||||
InsnArg arg = insn.getArg(0);
|
||||
InsnArg arg = insn.getArg(1);
|
||||
// "++" or "--"
|
||||
if (arg.isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {
|
||||
LiteralArg lit = (LiteralArg) arg;
|
||||
|
||||
@@ -78,8 +78,8 @@ public class MethodGen {
|
||||
if (clsAccFlags.isAnnotation()) {
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
code.startLine(ai.makeString());
|
||||
code.attachSourceLine(mth.getSourceLine());
|
||||
code.startLineWithNum(mth.getSourceLine());
|
||||
code.add(ai.makeString());
|
||||
|
||||
if (classGen.addGenericMap(code, mth.getGenericMap())) {
|
||||
code.add(' ');
|
||||
@@ -206,13 +206,10 @@ public class MethodGen {
|
||||
if (insn == null) {
|
||||
continue;
|
||||
}
|
||||
if (addLabels) {
|
||||
if (insn.contains(AType.JUMP)
|
||||
|| insn.contains(AType.EXC_HANDLER)) {
|
||||
code.decIndent();
|
||||
code.startLine(getLabelName(insn.getOffset()) + ":");
|
||||
code.incIndent();
|
||||
}
|
||||
if (addLabels && (insn.contains(AType.JUMP) || insn.contains(AType.EXC_HANDLER))) {
|
||||
code.decIndent();
|
||||
code.startLine(getLabelName(insn.getOffset()) + ":");
|
||||
code.incIndent();
|
||||
}
|
||||
try {
|
||||
if (insnGen.makeInsn(insn, code)) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -53,10 +54,7 @@ public class NameGen {
|
||||
return name;
|
||||
}
|
||||
name = getUniqueVarName(name);
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar != null) {
|
||||
sVar.setName(name);
|
||||
}
|
||||
arg.setName(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -71,7 +69,16 @@ public class NameGen {
|
||||
}
|
||||
|
||||
public String useArg(RegisterArg arg) {
|
||||
String name = makeArgName(arg);
|
||||
String name = arg.getName();
|
||||
if (name == null) {
|
||||
return getFallbackName(arg);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// TODO: avoid name collision with variables names
|
||||
public String getLoopLabel(LoopLabelAttr attr) {
|
||||
String name = "loop" + attr.getLoop().getId();
|
||||
varNames.add(name);
|
||||
return name;
|
||||
}
|
||||
@@ -88,14 +95,10 @@ public class NameGen {
|
||||
}
|
||||
|
||||
private String makeArgName(RegisterArg arg) {
|
||||
String name = arg.getName();
|
||||
if (fallback) {
|
||||
String base = "r" + arg.getRegNum();
|
||||
if (name != null && !name.equals("this")) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return base;
|
||||
return getFallbackName(arg);
|
||||
}
|
||||
String name = arg.getName();
|
||||
String varName;
|
||||
if (name != null) {
|
||||
if ("this".equals(name)) {
|
||||
@@ -111,6 +114,15 @@ public class NameGen {
|
||||
return varName;
|
||||
}
|
||||
|
||||
private String getFallbackName(RegisterArg arg) {
|
||||
String name = arg.getName();
|
||||
String base = "r" + arg.getRegNum();
|
||||
if (name != null && !name.equals("this")) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private static String makeNameForType(ArgType type) {
|
||||
if (type.isPrimitive()) {
|
||||
return makeNameForPrimitive(type);
|
||||
|
||||
@@ -4,27 +4,35 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.LoopRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.SwitchRegion;
|
||||
import jadx.core.dex.regions.SynchronizedRegion;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.regions.TryCatchRegion;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.regions.loops.ForEachLoop;
|
||||
import jadx.core.dex.regions.loops.ForLoop;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.regions.loops.LoopType;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -52,6 +60,8 @@ public class RegionGen extends InsnGen {
|
||||
makeSwitch((SwitchRegion) cont, code);
|
||||
} else if (cont instanceof LoopRegion) {
|
||||
makeLoop((LoopRegion) cont, code);
|
||||
} else if (cont instanceof TryCatchRegion) {
|
||||
makeTryCatch((TryCatchRegion) cont, code);
|
||||
} else if (cont instanceof SynchronizedRegion) {
|
||||
makeSynchronizedRegion((SynchronizedRegion) cont, code);
|
||||
}
|
||||
@@ -73,14 +83,9 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private void makeSimpleRegion(CodeWriter code, Region region) throws CodegenException {
|
||||
CatchAttr tc = region.get(AType.CATCH_BLOCK);
|
||||
if (tc != null) {
|
||||
makeTryCatch(region, tc.getTryBlock(), code);
|
||||
} else {
|
||||
declareVars(code, region);
|
||||
for (IContainer c : region.getSubBlocks()) {
|
||||
makeRegion(code, c);
|
||||
}
|
||||
declareVars(code, region);
|
||||
for (IContainer c : region.getSubBlocks()) {
|
||||
makeRegion(code, c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +97,9 @@ public class RegionGen extends InsnGen {
|
||||
|
||||
private void makeSimpleBlock(IBlock block, CodeWriter code) throws CodegenException {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
makeInsn(insn, code);
|
||||
if (!insn.contains(AFlag.SKIP)) {
|
||||
makeInsn(insn, code);
|
||||
}
|
||||
}
|
||||
ForceReturnAttr retAttr = block.get(AType.FORCE_RETURN);
|
||||
if (retAttr != null) {
|
||||
@@ -101,14 +108,11 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private void makeIf(IfRegion region, CodeWriter code, boolean newLine) throws CodegenException {
|
||||
if (region.getTernRegion() != null) {
|
||||
makeSimpleBlock(region.getTernRegion().getBlock(), code);
|
||||
return;
|
||||
}
|
||||
if (newLine) {
|
||||
code.startLine();
|
||||
code.startLineWithNum(region.getSourceLine());
|
||||
} else {
|
||||
code.attachSourceLine(region.getSourceLine());
|
||||
}
|
||||
code.attachSourceLine(region.getSourceLine());
|
||||
code.add("if (");
|
||||
new ConditionGen(this).add(code, region.getCondition());
|
||||
code.add(") {");
|
||||
@@ -151,8 +155,7 @@ public class RegionGen extends InsnGen {
|
||||
if (header != null) {
|
||||
List<InsnNode> headerInsns = header.getInstructions();
|
||||
if (headerInsns.size() > 1) {
|
||||
// write not inlined instructions from header
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
ErrorsCounter.methodError(mth, "Found not inlined instructions from loop header");
|
||||
int last = headerInsns.size() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
InsnNode insn = headerInsns.get(i);
|
||||
@@ -160,6 +163,10 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
}
|
||||
}
|
||||
LoopLabelAttr labelAttr = region.getInfo().getStart().get(AType.LOOP_LABEL);
|
||||
if (labelAttr != null) {
|
||||
code.startLine(mgen.getNameGen().getLoopLabel(labelAttr)).add(':');
|
||||
}
|
||||
|
||||
IfCondition condition = region.getCondition();
|
||||
if (condition == null) {
|
||||
@@ -169,8 +176,35 @@ public class RegionGen extends InsnGen {
|
||||
code.startLine('}');
|
||||
return code;
|
||||
}
|
||||
|
||||
ConditionGen conditionGen = new ConditionGen(this);
|
||||
LoopType type = region.getType();
|
||||
if (type != null) {
|
||||
if (type instanceof ForLoop) {
|
||||
ForLoop forLoop = (ForLoop) type;
|
||||
code.startLine("for (");
|
||||
makeInsn(forLoop.getInitInsn(), code, Flags.INLINE);
|
||||
code.add("; ");
|
||||
conditionGen.add(code, condition);
|
||||
code.add("; ");
|
||||
makeInsn(forLoop.getIncrInsn(), code, Flags.INLINE);
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region.getBody());
|
||||
code.startLine('}');
|
||||
return code;
|
||||
}
|
||||
if (type instanceof ForEachLoop) {
|
||||
ForEachLoop forEachLoop = (ForEachLoop) type;
|
||||
code.startLine("for (");
|
||||
declareVar(code, forEachLoop.getVarArg());
|
||||
code.add(" : ");
|
||||
addArg(code, forEachLoop.getIterableArg(), false);
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region.getBody());
|
||||
code.startLine('}');
|
||||
return code;
|
||||
}
|
||||
throw new JadxRuntimeException("Unknown loop type: " + type.getClass());
|
||||
}
|
||||
if (region.isConditionAtEnd()) {
|
||||
code.startLine("do {");
|
||||
makeRegionIndent(code, region.getBody());
|
||||
@@ -209,7 +243,14 @@ public class RegionGen extends InsnGen {
|
||||
IContainer c = sw.getCases().get(i);
|
||||
for (Object k : keys) {
|
||||
code.startLine("case ");
|
||||
if (k instanceof IndexInsnNode) {
|
||||
if (k instanceof FieldNode) {
|
||||
FieldNode fn = (FieldNode) k;
|
||||
if (fn.getParentClass().isEnum()) {
|
||||
code.add(fn.getName());
|
||||
} else {
|
||||
staticField(code, fn.getFieldInfo());
|
||||
}
|
||||
} else if (k instanceof IndexInsnNode) {
|
||||
staticField(code, (FieldInfo) ((IndexInsnNode) k).getIndex());
|
||||
} else {
|
||||
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
|
||||
@@ -240,18 +281,18 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void makeTryCatch(IContainer region, TryCatchBlock tryCatchBlock, CodeWriter code)
|
||||
throws CodegenException {
|
||||
private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException {
|
||||
TryCatchBlock tryCatchBlock = region.geTryCatchBlock();
|
||||
code.startLine("try {");
|
||||
region.remove(AType.CATCH_BLOCK);
|
||||
makeRegionIndent(code, region);
|
||||
makeRegionIndent(code, region.getTryRegion());
|
||||
// TODO: move search of 'allHandler' to 'TryCatchRegion'
|
||||
ExceptionHandler allHandler = null;
|
||||
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
||||
if (!handler.isCatchAll()) {
|
||||
makeCatchBlock(code, handler);
|
||||
} else {
|
||||
if (allHandler != null) {
|
||||
LOG.warn("Several 'all' handlers in try/catch block in " + mth);
|
||||
LOG.warn("Several 'all' handlers in try/catch block in {}", mth);
|
||||
}
|
||||
allHandler = handler;
|
||||
}
|
||||
@@ -266,20 +307,25 @@ public class RegionGen extends InsnGen {
|
||||
code.startLine('}');
|
||||
}
|
||||
|
||||
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler)
|
||||
throws CodegenException {
|
||||
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler) throws CodegenException {
|
||||
IContainer region = handler.getHandlerRegion();
|
||||
if (region != null) {
|
||||
code.startLine("} catch (");
|
||||
if (region == null) {
|
||||
return;
|
||||
}
|
||||
code.startLine("} catch (");
|
||||
InsnArg arg = handler.getArg();
|
||||
if (arg instanceof RegisterArg) {
|
||||
declareVar(code, (RegisterArg) arg);
|
||||
} else if (arg instanceof NamedArg) {
|
||||
if (handler.isCatchAll()) {
|
||||
code.add("Throwable");
|
||||
} else {
|
||||
useClass(code, handler.getCatchType());
|
||||
}
|
||||
code.add(' ');
|
||||
code.add(mgen.getNameGen().assignNamedArg(handler.getArg()));
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region);
|
||||
code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
|
||||
}
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@ import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TypeGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TypeGen.class);
|
||||
|
||||
private TypeGen() {
|
||||
}
|
||||
|
||||
public static String signature(ArgType type) {
|
||||
PrimitiveType stype = type.getPrimitiveType();
|
||||
@@ -56,7 +63,8 @@ public class TypeGen {
|
||||
case OBJECT:
|
||||
case ARRAY:
|
||||
if (lit != 0) {
|
||||
throw new JadxRuntimeException("Wrong object literal: " + type + " = " + lit);
|
||||
LOG.warn("Wrong object literal: " + lit + " for type: " + type);
|
||||
return Long.toString(lit);
|
||||
}
|
||||
return "null";
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public enum AFlag {
|
||||
DONT_WRAP,
|
||||
|
||||
DONT_SHRINK,
|
||||
DONT_INLINE,
|
||||
DONT_GENERATE,
|
||||
SKIP,
|
||||
|
||||
@@ -23,5 +24,8 @@ public enum AFlag {
|
||||
|
||||
ELSE_IF_CHAIN,
|
||||
|
||||
WRAPPED,
|
||||
ARITH_ONEARG,
|
||||
|
||||
INCONSISTENT_CODE, // warning about incorrect decompilation
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
@@ -28,6 +30,8 @@ public class AType<T extends IAttribute> {
|
||||
private AType() {
|
||||
}
|
||||
|
||||
public static final int FIELDS_COUNT = 18;
|
||||
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<AttrList<JumpInfo>>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<AttrList<LoopInfo>>();
|
||||
|
||||
@@ -40,9 +44,11 @@ public class AType<T extends IAttribute> {
|
||||
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<JadxErrorAttr>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<EnumClassAttr>();
|
||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<EnumMapAttr>();
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<AnnotationsList>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<MethodParameters>();
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<PhiListAttr>();
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<LoopLabelAttr>();
|
||||
}
|
||||
|
||||
@@ -12,25 +12,25 @@ public abstract class AttrNode implements IAttributeNode {
|
||||
|
||||
@Override
|
||||
public void add(AFlag flag) {
|
||||
getStorage().add(flag);
|
||||
initStorage().add(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttr(IAttribute attr) {
|
||||
getStorage().add(attr);
|
||||
initStorage().add(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addAttr(AType<AttrList<T>> type, T obj) {
|
||||
getStorage().add(type, obj);
|
||||
initStorage().add(type, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAttributesFrom(AttrNode attrNode) {
|
||||
getStorage().addAll(attrNode.storage);
|
||||
initStorage().addAll(attrNode.storage);
|
||||
}
|
||||
|
||||
AttributeStorage getStorage() {
|
||||
AttributeStorage initStorage() {
|
||||
AttributeStorage store = storage;
|
||||
if (store == EMPTY_ATTR_STORAGE) {
|
||||
store = new AttributeStorage();
|
||||
|
||||
@@ -7,7 +7,7 @@ import jadx.core.utils.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -24,7 +24,7 @@ public class AttributeStorage {
|
||||
|
||||
public AttributeStorage() {
|
||||
flags = EnumSet.noneOf(AFlag.class);
|
||||
attributes = new HashMap<AType<?>, IAttribute>(2);
|
||||
attributes = new IdentityHashMap<AType<?>, IAttribute>(AType.FIELDS_COUNT);
|
||||
}
|
||||
|
||||
public void add(AFlag flag) {
|
||||
@@ -72,7 +72,7 @@ public class AttributeStorage {
|
||||
if (attrList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return attrList.getList();
|
||||
return Collections.unmodifiableList(attrList.getList());
|
||||
}
|
||||
|
||||
public void remove(AFlag flag) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class EmptyAttrStorage extends AttributeStorage {
|
||||
public final class EmptyAttrStorage extends AttributeStorage {
|
||||
|
||||
@Override
|
||||
public boolean contains(AFlag flag) {
|
||||
@@ -52,4 +52,9 @@ public class EmptyAttrStorage extends AttributeStorage {
|
||||
public List<String> getAttributeStrings() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ public class AnnotationsList implements IAttribute {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<AnnotationsList> getType() {
|
||||
return AType.ANNOTATION_LIST;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EnumMapAttr implements IAttribute {
|
||||
|
||||
public static class KeyValueMap {
|
||||
private Map<Object, Object> map = new HashMap<Object, Object>();
|
||||
|
||||
public Object get(Object key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
void put(Object key, Object value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<FieldNode, KeyValueMap> fieldsMap = new HashMap<FieldNode, KeyValueMap>();
|
||||
|
||||
public KeyValueMap getMap(FieldNode field) {
|
||||
return fieldsMap.get(field);
|
||||
}
|
||||
|
||||
public void add(FieldNode field, Object key, Object value) {
|
||||
KeyValueMap map = getMap(field);
|
||||
if (map == null) {
|
||||
map = new KeyValueMap();
|
||||
fieldsMap.put(field, map);
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<EnumMapAttr> getType() {
|
||||
return AType.ENUM_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Enum fields map: " + fieldsMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public class LoopInfo {
|
||||
private final BlockNode end;
|
||||
private final Set<BlockNode> loopBlocks;
|
||||
|
||||
private int id;
|
||||
private LoopInfo parentLoop;
|
||||
|
||||
public LoopInfo(BlockNode start, BlockNode end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
@@ -69,8 +72,24 @@ public class LoopInfo {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public LoopInfo getParentLoop() {
|
||||
return parentLoop;
|
||||
}
|
||||
|
||||
public void setParentLoop(LoopInfo parentLoop) {
|
||||
this.parentLoop = parentLoop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOOP: " + start + "->" + end;
|
||||
return "LOOP:" + id + ": " + start + "->" + end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
|
||||
public class LoopLabelAttr implements IAttribute {
|
||||
|
||||
private final LoopInfo loop;
|
||||
|
||||
public LoopLabelAttr(LoopInfo loop) {
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
public LoopInfo getLoop() {
|
||||
return loop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<LoopLabelAttr> getType() {
|
||||
return AType.LOOP_LABEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOOP_LABEL: " + loop;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -51,10 +52,8 @@ public class ArithNode extends InsnNode {
|
||||
}
|
||||
|
||||
public ArithNode(ArithOp op, RegisterArg res, InsnArg a) {
|
||||
super(InsnType.ARITH_ONEARG, 1);
|
||||
this.op = op;
|
||||
setResult(res);
|
||||
addArg(a);
|
||||
this(op, res, res, a);
|
||||
add(AFlag.ARITH_ONEARG);
|
||||
}
|
||||
|
||||
public ArithOp getOp() {
|
||||
@@ -85,7 +84,7 @@ public class ArithNode extends InsnNode {
|
||||
+ getResult() + " = "
|
||||
+ getArg(0) + " "
|
||||
+ op.getSymbol() + " "
|
||||
+ (getArgsCount() == 2 ? getArg(1) : "");
|
||||
+ getArg(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,14 @@ import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.io.EOFException;
|
||||
|
||||
import com.android.dex.Code;
|
||||
import com.android.dx.io.OpcodeInfo;
|
||||
import com.android.dx.io.Opcodes;
|
||||
@@ -44,7 +42,7 @@ public class InsnDecoder {
|
||||
while (in.hasMore()) {
|
||||
decoded[in.cursor()] = DecodedInstruction.decode(in);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException(method, "", e);
|
||||
}
|
||||
insnArr = decoded;
|
||||
@@ -406,8 +404,7 @@ public class InsnDecoder {
|
||||
|
||||
case Opcodes.MOVE_EXCEPTION:
|
||||
return insn(InsnType.MOVE_EXCEPTION,
|
||||
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)),
|
||||
new NamedArg("e", ArgType.unknown(PrimitiveType.OBJECT)));
|
||||
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)));
|
||||
|
||||
case Opcodes.RETURN_VOID:
|
||||
return new InsnNode(InsnType.RETURN, 0);
|
||||
@@ -624,16 +621,19 @@ public class InsnDecoder {
|
||||
int resReg = getMoveResultRegister(insnArr, offset);
|
||||
ArgType arrType = dex.getType(insn.getIndex());
|
||||
ArgType elType = arrType.getArrayElement();
|
||||
InsnArg[] regs = new InsnArg[insn.getRegisterCount()];
|
||||
boolean typeImmutable = elType.isPrimitive();
|
||||
int regsCount = insn.getRegisterCount();
|
||||
InsnArg[] regs = new InsnArg[regsCount];
|
||||
if (isRange) {
|
||||
int r = insn.getA();
|
||||
for (int i = 0; i < insn.getRegisterCount(); i++) {
|
||||
regs[i] = InsnArg.reg(r, elType);
|
||||
for (int i = 0; i < regsCount; i++) {
|
||||
regs[i] = InsnArg.reg(r, elType, typeImmutable);
|
||||
r++;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < insn.getRegisterCount(); i++) {
|
||||
regs[i] = InsnArg.reg(insn, i, elType);
|
||||
for (int i = 0; i < regsCount; i++) {
|
||||
int regNum = InsnUtils.getArg(insn, i);
|
||||
regs[i] = InsnArg.reg(regNum, elType, typeImmutable);
|
||||
}
|
||||
}
|
||||
return insn(InsnType.FILLED_NEW_ARRAY,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
public enum InsnType {
|
||||
NOP, // replacement for removed instructions
|
||||
|
||||
CONST,
|
||||
CONST_STR,
|
||||
@@ -48,17 +47,24 @@ public enum InsnType {
|
||||
|
||||
INVOKE,
|
||||
|
||||
// additional instructions
|
||||
// *** Additional instructions ***
|
||||
|
||||
// replacement for removed instructions
|
||||
NOP,
|
||||
|
||||
TERNARY,
|
||||
CONSTRUCTOR,
|
||||
|
||||
BREAK,
|
||||
CONTINUE,
|
||||
|
||||
STR_CONCAT, // strings concatenation
|
||||
ARITH_ONEARG,
|
||||
// strings concatenation
|
||||
STR_CONCAT,
|
||||
|
||||
TERNARY,
|
||||
ARGS, // just generate arguments
|
||||
// just generate one argument
|
||||
ONE_ARG,
|
||||
PHI,
|
||||
|
||||
NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function
|
||||
// TODO: now multidimensional arrays created using Array.newInstance function
|
||||
NEW_MULTIDIM_ARRAY
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -14,6 +15,7 @@ public class PhiInsn extends InsnNode {
|
||||
for (int i = 0; i < predecessors; i++) {
|
||||
addReg(regNum, ArgType.UNKNOWN);
|
||||
}
|
||||
add(AFlag.DONT_INLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -21,6 +23,14 @@ public class PhiInsn extends InsnNode {
|
||||
return (RegisterArg) super.getArg(n);
|
||||
}
|
||||
|
||||
public boolean removeArg(RegisterArg arg) {
|
||||
boolean isRemoved = super.removeArg(arg);
|
||||
if (isRemoved) {
|
||||
arg.getSVar().setUsedInPhi(null);
|
||||
}
|
||||
return isRemoved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments());
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ArgType {
|
||||
|
||||
public static final ArgType INT = primitive(PrimitiveType.INT);
|
||||
@@ -93,10 +95,28 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
private abstract static class KnownType extends ArgType {
|
||||
|
||||
private static final PrimitiveType[] EMPTY_POSSIBLES = new PrimitiveType[0];
|
||||
|
||||
@Override
|
||||
public boolean isTypeKnown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(PrimitiveType type) {
|
||||
return getPrimitiveType() == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType selectFirst() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimitiveType[] getPossibleTypes() {
|
||||
return EMPTY_POSSIBLES;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PrimitiveArg extends KnownType {
|
||||
@@ -269,6 +289,7 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
private static final class ArrayArg extends KnownType {
|
||||
public static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[]{PrimitiveType.ARRAY};
|
||||
private final ArgType arrayElement;
|
||||
|
||||
public ArrayArg(ArgType arrayElement) {
|
||||
@@ -291,6 +312,21 @@ public abstract class ArgType {
|
||||
return PrimitiveType.ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeKnown() {
|
||||
return arrayElement.isTypeKnown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType selectFirst() {
|
||||
return array(arrayElement.selectFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimitiveType[] getPossibleTypes() {
|
||||
return ARRAY_POSSIBLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArrayDimension() {
|
||||
return 1 + arrayElement.getArrayDimension();
|
||||
@@ -343,8 +379,10 @@ public abstract class ArgType {
|
||||
@Override
|
||||
public ArgType selectFirst() {
|
||||
PrimitiveType f = possibleTypes[0];
|
||||
if (f == PrimitiveType.OBJECT || f == PrimitiveType.ARRAY) {
|
||||
return object(Consts.CLASS_OBJECT);
|
||||
if (contains(PrimitiveType.OBJECT)) {
|
||||
return OBJECT;
|
||||
} else if (contains(PrimitiveType.ARRAY)) {
|
||||
return array(OBJECT);
|
||||
} else {
|
||||
return primitive(f);
|
||||
}
|
||||
@@ -428,18 +466,13 @@ public abstract class ArgType {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean contains(PrimitiveType type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public abstract boolean contains(PrimitiveType type);
|
||||
|
||||
public ArgType selectFirst() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public abstract ArgType selectFirst();
|
||||
|
||||
public PrimitiveType[] getPossibleTypes() {
|
||||
return null;
|
||||
}
|
||||
public abstract PrimitiveType[] getPossibleTypes();
|
||||
|
||||
@Nullable
|
||||
public static ArgType merge(ArgType a, ArgType b) {
|
||||
if (a == null || b == null) {
|
||||
return null;
|
||||
@@ -458,13 +491,18 @@ public abstract class ArgType {
|
||||
if (a == UNKNOWN) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (a.isArray()) {
|
||||
return mergeArrays((ArrayArg) a, b);
|
||||
} else if (b.isArray()) {
|
||||
return mergeArrays((ArrayArg) b, a);
|
||||
}
|
||||
if (!a.isTypeKnown()) {
|
||||
if (b.isTypeKnown()) {
|
||||
if (a.contains(b.getPrimitiveType())) {
|
||||
return b;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
// both types unknown
|
||||
List<PrimitiveType> types = new ArrayList<PrimitiveType>();
|
||||
@@ -475,7 +513,8 @@ public abstract class ArgType {
|
||||
}
|
||||
if (types.isEmpty()) {
|
||||
return null;
|
||||
} else if (types.size() == 1) {
|
||||
}
|
||||
if (types.size() == 1) {
|
||||
PrimitiveType nt = types.get(0);
|
||||
if (nt == PrimitiveType.OBJECT || nt == PrimitiveType.ARRAY) {
|
||||
return unknown(nt);
|
||||
@@ -499,31 +538,15 @@ public abstract class ArgType {
|
||||
String bObj = b.getObject();
|
||||
if (aObj.equals(bObj)) {
|
||||
return a.getGenericTypes() != null ? a : b;
|
||||
} else if (aObj.equals(Consts.CLASS_OBJECT)) {
|
||||
}
|
||||
if (aObj.equals(Consts.CLASS_OBJECT)) {
|
||||
return b;
|
||||
} else if (bObj.equals(Consts.CLASS_OBJECT)) {
|
||||
}
|
||||
if (bObj.equals(Consts.CLASS_OBJECT)) {
|
||||
return a;
|
||||
} else {
|
||||
// different objects
|
||||
String obj = clsp.getCommonAncestor(aObj, bObj);
|
||||
return obj == null ? null : object(obj);
|
||||
}
|
||||
}
|
||||
if (a.isArray()) {
|
||||
if (b.isArray()) {
|
||||
ArgType ea = a.getArrayElement();
|
||||
ArgType eb = b.getArrayElement();
|
||||
if (ea.isPrimitive() && eb.isPrimitive()) {
|
||||
return OBJECT;
|
||||
} else {
|
||||
ArgType res = merge(ea, eb);
|
||||
return res == null ? null : array(res);
|
||||
}
|
||||
} else if (b.equals(OBJECT)) {
|
||||
return OBJECT;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
String obj = clsp.getCommonAncestor(aObj, bObj);
|
||||
return obj == null ? null : object(obj);
|
||||
}
|
||||
if (a.isPrimitive() && b.isPrimitive() && a.getRegCount() == b.getRegCount()) {
|
||||
return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType()));
|
||||
@@ -532,6 +555,25 @@ public abstract class ArgType {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ArgType mergeArrays(ArrayArg array, ArgType b) {
|
||||
if (b.isArray()) {
|
||||
ArgType ea = array.getArrayElement();
|
||||
ArgType eb = b.getArrayElement();
|
||||
if (ea.isPrimitive() && eb.isPrimitive()) {
|
||||
return OBJECT;
|
||||
}
|
||||
ArgType res = merge(ea, eb);
|
||||
return res == null ? null : array(res);
|
||||
}
|
||||
if (b.contains(PrimitiveType.ARRAY)) {
|
||||
return array;
|
||||
}
|
||||
if (b.equals(OBJECT)) {
|
||||
return OBJECT;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isCastNeeded(ArgType from, ArgType to) {
|
||||
if (from.equals(to)) {
|
||||
return false;
|
||||
@@ -543,6 +585,16 @@ public abstract class ArgType {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isInstanceOf(ArgType type, ArgType of) {
|
||||
if (type.equals(of)) {
|
||||
return true;
|
||||
}
|
||||
if (!type.isObject() || !of.isObject()) {
|
||||
return false;
|
||||
}
|
||||
return clsp.isImplements(type.getObject(), of.getObject());
|
||||
}
|
||||
|
||||
public static ArgType parse(String type) {
|
||||
char f = type.charAt(0);
|
||||
switch (f) {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -26,8 +30,12 @@ public abstract class InsnArg extends Typed {
|
||||
return reg(InsnUtils.getArg(insn, argNum), type);
|
||||
}
|
||||
|
||||
public static MthParameterArg parameterReg(int regNum, ArgType type) {
|
||||
return new MthParameterArg(regNum, type);
|
||||
public static TypeImmutableArg typeImmutableReg(int regNum, ArgType type) {
|
||||
return new TypeImmutableArg(regNum, type);
|
||||
}
|
||||
|
||||
public static RegisterArg reg(int regNum, ArgType type, boolean typeImmutable) {
|
||||
return typeImmutable ? new TypeImmutableArg(regNum, type) : new RegisterArg(regNum, type);
|
||||
}
|
||||
|
||||
public static LiteralArg lit(long literal, ArgType type) {
|
||||
@@ -76,18 +84,36 @@ public abstract class InsnArg extends Typed {
|
||||
return null;
|
||||
}
|
||||
if (parent == insn) {
|
||||
LOG.debug("Can't wrap instruction info itself: " + insn);
|
||||
LOG.debug("Can't wrap instruction info itself: {}", insn);
|
||||
Thread.dumpStack();
|
||||
return null;
|
||||
}
|
||||
int i = getArgIndex(parent, this);
|
||||
if (i == -1) {
|
||||
return null;
|
||||
}
|
||||
insn.add(AFlag.WRAPPED);
|
||||
InsnArg arg = wrapArg(insn);
|
||||
parent.setArg(i, arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
public static void updateParentInsn(InsnNode fromInsn, InsnNode toInsn) {
|
||||
List<RegisterArg> args = new ArrayList<RegisterArg>();
|
||||
fromInsn.getRegisterArgs(args);
|
||||
for (RegisterArg reg : args) {
|
||||
reg.setParentInsn(toInsn);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getArgIndex(InsnNode parent, InsnArg arg) {
|
||||
int count = parent.getArgsCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (parent.getArg(i) == this) {
|
||||
InsnArg arg = wrapArg(insn);
|
||||
parent.setArg(i, arg);
|
||||
return arg;
|
||||
if (parent.getArg(i) == arg) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static InsnArg wrapArg(InsnNode insn) {
|
||||
|
||||
@@ -38,11 +38,11 @@ public final class LiteralArg extends InsnArg {
|
||||
|
||||
public boolean isInteger() {
|
||||
PrimitiveType type = this.type.getPrimitiveType();
|
||||
return (type == PrimitiveType.INT
|
||||
return type == PrimitiveType.INT
|
||||
|| type == PrimitiveType.BYTE
|
||||
|| type == PrimitiveType.CHAR
|
||||
|| type == PrimitiveType.SHORT
|
||||
|| type == PrimitiveType.LONG);
|
||||
|| type == PrimitiveType.LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,7 +19,8 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
|
||||
|
||||
protected final int regNum;
|
||||
protected SSAVar sVar;
|
||||
// not null after SSATransform pass
|
||||
private SSAVar sVar;
|
||||
|
||||
public RegisterArg(int rn) {
|
||||
this.regNum = rn;
|
||||
@@ -80,8 +81,10 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public void forceType(ArgType type) {
|
||||
this.type = type;
|
||||
public RegisterArg duplicate() {
|
||||
RegisterArg dup = new RegisterArg(getRegNum(), getType());
|
||||
dup.setSVar(sVar);
|
||||
return dup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,11 +141,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
}
|
||||
RegisterArg assign = sVar.getAssign();
|
||||
if (assign != null) {
|
||||
return assign.getParentInsn();
|
||||
}
|
||||
return null;
|
||||
return sVar.getAssign().getParentInsn();
|
||||
}
|
||||
|
||||
public InsnNode getPhiAssignInsn() {
|
||||
@@ -150,12 +149,9 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
if (usePhi != null) {
|
||||
return usePhi;
|
||||
}
|
||||
RegisterArg assign = sVar.getAssign();
|
||||
if (assign != null) {
|
||||
InsnNode parent = assign.getParentInsn();
|
||||
if (parent != null && parent.getType() == InsnType.PHI) {
|
||||
return parent;
|
||||
}
|
||||
InsnNode parent = sVar.getAssign().getParentInsn();
|
||||
if (parent != null && parent.getType() == InsnType.PHI) {
|
||||
return parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,25 +5,74 @@ import jadx.core.dex.instructions.PhiInsn;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SSAVar {
|
||||
|
||||
private final int regNum;
|
||||
private final int version;
|
||||
private VarName varName;
|
||||
|
||||
private int startUseAddr;
|
||||
private int endUseAddr;
|
||||
|
||||
@NotNull
|
||||
private RegisterArg assign;
|
||||
private final List<RegisterArg> useList = new ArrayList<RegisterArg>(2);
|
||||
@Nullable
|
||||
private PhiInsn usedInPhi;
|
||||
|
||||
private ArgType type;
|
||||
private boolean typeImmutable;
|
||||
|
||||
public SSAVar(int regNum, int v, RegisterArg assign) {
|
||||
public SSAVar(int regNum, int v, @NotNull RegisterArg assign) {
|
||||
this.regNum = regNum;
|
||||
this.version = v;
|
||||
this.assign = assign;
|
||||
|
||||
if (assign != null) {
|
||||
assign.setSVar(this);
|
||||
assign.setSVar(this);
|
||||
startUseAddr = -1;
|
||||
endUseAddr = -1;
|
||||
}
|
||||
|
||||
public int getStartAddr() {
|
||||
if (startUseAddr == -1) {
|
||||
calcUsageAddrRange();
|
||||
}
|
||||
return startUseAddr;
|
||||
}
|
||||
|
||||
public int getEndAddr() {
|
||||
if (endUseAddr == -1) {
|
||||
calcUsageAddrRange();
|
||||
}
|
||||
return endUseAddr;
|
||||
}
|
||||
|
||||
private void calcUsageAddrRange() {
|
||||
int start = Integer.MAX_VALUE;
|
||||
int end = Integer.MIN_VALUE;
|
||||
|
||||
if (assign.getParentInsn() != null) {
|
||||
int insnAddr = assign.getParentInsn().getOffset();
|
||||
if (insnAddr >= 0) {
|
||||
start = Math.min(insnAddr, start);
|
||||
end = Math.max(insnAddr, end);
|
||||
}
|
||||
}
|
||||
for (RegisterArg arg : useList) {
|
||||
if (arg.getParentInsn() != null) {
|
||||
int insnAddr = arg.getParentInsn().getOffset();
|
||||
if (insnAddr >= 0) {
|
||||
start = Math.min(insnAddr, start);
|
||||
end = Math.max(insnAddr, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
|
||||
startUseAddr = start;
|
||||
endUseAddr = end;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +84,12 @@ public class SSAVar {
|
||||
return version;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RegisterArg getAssign() {
|
||||
return assign;
|
||||
}
|
||||
|
||||
public void setAssign(RegisterArg assign) {
|
||||
public void setAssign(@NotNull RegisterArg assign) {
|
||||
this.assign = assign;
|
||||
}
|
||||
|
||||
@@ -68,10 +118,11 @@ public class SSAVar {
|
||||
}
|
||||
}
|
||||
|
||||
public void setUsedInPhi(PhiInsn usedInPhi) {
|
||||
public void setUsedInPhi(@Nullable PhiInsn usedInPhi) {
|
||||
this.usedInPhi = usedInPhi;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PhiInsn getUsedInPhi() {
|
||||
return usedInPhi;
|
||||
}
|
||||
@@ -81,24 +132,34 @@ public class SSAVar {
|
||||
}
|
||||
|
||||
public int getVariableUseCount() {
|
||||
if (!isUsedInPhi()) {
|
||||
if (usedInPhi == null) {
|
||||
return useList.size();
|
||||
}
|
||||
return useList.size() + usedInPhi.getResult().getSVar().getUseCount();
|
||||
}
|
||||
|
||||
public ArgType getType() {
|
||||
return type;
|
||||
public void setType(ArgType type) {
|
||||
ArgType acceptedType;
|
||||
if (typeImmutable) {
|
||||
// don't change type, just update types in useList
|
||||
acceptedType = this.type;
|
||||
} else {
|
||||
acceptedType = type;
|
||||
this.type = acceptedType;
|
||||
}
|
||||
assign.type = acceptedType;
|
||||
for (int i = 0, useListSize = useList.size(); i < useListSize; i++) {
|
||||
useList.get(i).type = acceptedType;
|
||||
}
|
||||
}
|
||||
|
||||
public void setType(ArgType type) {
|
||||
this.type = type;
|
||||
if (assign != null) {
|
||||
assign.type = type;
|
||||
}
|
||||
for (int i = 0, useListSize = useList.size(); i < useListSize; i++) {
|
||||
useList.get(i).type = type;
|
||||
}
|
||||
public void setTypeImmutable(ArgType type) {
|
||||
setType(type);
|
||||
this.typeImmutable = true;
|
||||
}
|
||||
|
||||
public boolean isTypeImmutable() {
|
||||
return typeImmutable;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
|
||||
+6
-4
@@ -1,10 +1,10 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public class MthParameterArg extends RegisterArg {
|
||||
public class TypeImmutableArg extends RegisterArg {
|
||||
|
||||
private boolean isThis;
|
||||
|
||||
public MthParameterArg(int rn, ArgType type) {
|
||||
public TypeImmutableArg(int rn, ArgType type) {
|
||||
super(rn, type);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class MthParameterArg extends RegisterArg {
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
// not allowed
|
||||
}
|
||||
|
||||
public void markAsThis() {
|
||||
@@ -39,6 +40,7 @@ public class MthParameterArg extends RegisterArg {
|
||||
if (isThis) {
|
||||
sVar.setName("this");
|
||||
}
|
||||
sVar.setTypeImmutable(type);
|
||||
super.setSVar(sVar);
|
||||
}
|
||||
|
||||
@@ -47,13 +49,13 @@ public class MthParameterArg extends RegisterArg {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MthParameterArg)) {
|
||||
if (!(obj instanceof TypeImmutableArg)) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
MthParameterArg that = (MthParameterArg) obj;
|
||||
TypeImmutableArg that = (TypeImmutableArg) obj;
|
||||
return isThis == that.isThis;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,13 @@ public class ConstructorInsn extends InsnNode {
|
||||
setSourceLine(invoke.getSourceLine());
|
||||
}
|
||||
|
||||
public ConstructorInsn(MethodInfo callMth, CallType callType, RegisterArg instanceArg) {
|
||||
super(InsnType.CONSTRUCTOR, callMth.getArgsCount());
|
||||
this.callMth = callMth;
|
||||
this.callType = callType;
|
||||
this.instanceArg = instanceArg;
|
||||
}
|
||||
|
||||
public MethodInfo getCallMth() {
|
||||
return callMth;
|
||||
}
|
||||
@@ -64,6 +71,14 @@ public class ConstructorInsn extends InsnNode {
|
||||
return callMth.getDeclClass();
|
||||
}
|
||||
|
||||
public CallType getCallType() {
|
||||
return callType;
|
||||
}
|
||||
|
||||
public boolean isNewInstance() {
|
||||
return callType == CallType.CONSTRUCTOR;
|
||||
}
|
||||
|
||||
public boolean isSuper() {
|
||||
return callType == CallType.SUPER;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,19 @@ import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class TernaryInsn extends InsnNode {
|
||||
import java.util.List;
|
||||
|
||||
private final IfCondition condition;
|
||||
public final class TernaryInsn extends InsnNode {
|
||||
|
||||
private IfCondition condition;
|
||||
|
||||
public TernaryInsn(IfCondition condition, RegisterArg result) {
|
||||
this(condition, result, LiteralArg.TRUE, LiteralArg.FALSE);
|
||||
}
|
||||
|
||||
public TernaryInsn(IfCondition condition, RegisterArg result, InsnArg th, InsnArg els) {
|
||||
super(InsnType.TERNARY, 2);
|
||||
@@ -33,6 +39,26 @@ public class TernaryInsn extends InsnNode {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void simplifyCondition() {
|
||||
condition = IfCondition.simplify(condition);
|
||||
if (condition.getMode() == IfCondition.Mode.NOT) {
|
||||
invert();
|
||||
}
|
||||
}
|
||||
|
||||
private void invert() {
|
||||
condition = IfCondition.invert(condition);
|
||||
InsnArg tmp = getArg(0);
|
||||
setArg(0, getArg(1));
|
||||
setArg(1, tmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRegisterArgs(List<RegisterArg> list) {
|
||||
super.getRegisterArgs(list);
|
||||
list.addAll(condition.getRegisterArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
|
||||
@@ -132,7 +132,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
try {
|
||||
new AnnotationsParser(this).parse(offset);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error parsing annotations in " + this, e);
|
||||
LOG.error("Error parsing annotations in {}", this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
}
|
||||
} catch (JadxRuntimeException e) {
|
||||
LOG.error("Class signature parse error: " + this, e);
|
||||
LOG.error("Class signature parse error: {}", this, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
field.setType(gType);
|
||||
}
|
||||
} catch (JadxRuntimeException e) {
|
||||
LOG.error("Field signature parse error: " + field, e);
|
||||
LOG.error("Field signature parse error: {}", field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +354,11 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
return parentClass;
|
||||
}
|
||||
|
||||
public ClassNode getTopParentClass() {
|
||||
ClassNode parent = getParentClass();
|
||||
return parent == this ? this : parent.getParentClass();
|
||||
}
|
||||
|
||||
public List<ClassNode> getInnerClasses() {
|
||||
return innerClasses;
|
||||
}
|
||||
@@ -377,10 +382,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
|
||||
public MethodNode getDefaultConstructor() {
|
||||
for (MethodNode mth : methods) {
|
||||
if (mth.getAccessFlags().isConstructor()
|
||||
&& mth.getMethodInfo().isConstructor()
|
||||
&& (mth.getMethodInfo().getArgsCount() == 0
|
||||
|| (mth.getArguments(false) != null && mth.getArguments(false).isEmpty()))) {
|
||||
if (mth.isDefaultConstructor()) {
|
||||
return mth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
@@ -31,16 +33,12 @@ public class DexNode {
|
||||
private final RootNode root;
|
||||
private final Dex dexBuf;
|
||||
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
private final String[] strings;
|
||||
|
||||
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
|
||||
|
||||
public DexNode(RootNode root, InputFile input) {
|
||||
this.root = root;
|
||||
this.dexBuf = input.getDexBuffer();
|
||||
|
||||
List<String> stringList = dexBuf.strings();
|
||||
this.strings = stringList.toArray(new String[stringList.size()]);
|
||||
}
|
||||
|
||||
public void loadClasses() throws DecodeException {
|
||||
@@ -53,10 +51,12 @@ public class DexNode {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassNode resolveClass(ClassInfo clsInfo) {
|
||||
return root.resolveClass(clsInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodNode resolveMethod(MethodInfo mth) {
|
||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||
if (cls != null) {
|
||||
@@ -65,6 +65,7 @@ public class DexNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode resolveField(FieldInfo field) {
|
||||
ClassNode cls = resolveClass(field.getDeclClass());
|
||||
if (cls != null) {
|
||||
@@ -80,7 +81,7 @@ public class DexNode {
|
||||
// DexBuffer wrappers
|
||||
|
||||
public String getString(int index) {
|
||||
return strings[index];
|
||||
return dexBuf.strings().get(index);
|
||||
}
|
||||
|
||||
public ArgType getType(int index) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import jadx.core.dex.attributes.IAttributeNode;
|
||||
|
||||
public interface IContainer extends IAttributeNode {
|
||||
|
||||
// unique id for use in 'toString()' method
|
||||
/**
|
||||
* Unique id for use in 'toString()' method
|
||||
*/
|
||||
String baseString();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ public class InsnNode extends LineAttrNode {
|
||||
}
|
||||
}
|
||||
|
||||
public static InsnNode wrapArg(InsnArg arg) {
|
||||
InsnNode insn = new InsnNode(InsnType.ONE_ARG, 1);
|
||||
insn.addArg(arg);
|
||||
return insn;
|
||||
}
|
||||
|
||||
public void setResult(RegisterArg res) {
|
||||
if (res != null) {
|
||||
res.setParentInsn(this);
|
||||
@@ -101,6 +107,17 @@ public class InsnNode extends LineAttrNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean removeArg(InsnArg arg) {
|
||||
int count = getArgsCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (arg == arguments.get(i)) {
|
||||
arguments.remove(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void addReg(DecodedInstruction insn, int i, ArgType type) {
|
||||
addArg(InsnArg.reg(insn, i, type));
|
||||
}
|
||||
@@ -135,6 +152,18 @@ public class InsnNode extends LineAttrNode {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConstInsn() {
|
||||
switch (getType()) {
|
||||
case CONST:
|
||||
case CONST_STR:
|
||||
case CONST_CLASS:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canReorder() {
|
||||
switch (getType()) {
|
||||
case CONST:
|
||||
@@ -153,7 +182,6 @@ public class InsnNode extends LineAttrNode {
|
||||
case NEW_ARRAY:
|
||||
case NEW_MULTIDIM_ARRAY:
|
||||
case STR_CONCAT:
|
||||
case MOVE_EXCEPTION:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
@@ -15,9 +15,9 @@ import jadx.core.dex.instructions.InsnDecoder;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.MthParameterArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.args.TypeImmutableArg;
|
||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
@@ -34,6 +34,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -52,6 +53,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
private final Method methodData;
|
||||
private int regsCount;
|
||||
private InsnNode[] instructions;
|
||||
private int codeSize;
|
||||
private int debugInfoOffset;
|
||||
private boolean noCode;
|
||||
|
||||
@@ -82,6 +84,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
try {
|
||||
if (noCode) {
|
||||
regsCount = 0;
|
||||
codeSize = 0;
|
||||
initMethodTypes();
|
||||
return;
|
||||
}
|
||||
@@ -94,6 +97,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
InsnDecoder decoder = new InsnDecoder(this);
|
||||
decoder.decodeInsns(mthCode);
|
||||
instructions = decoder.process();
|
||||
codeSize = instructions.length;
|
||||
|
||||
initTryCatches(mthCode);
|
||||
initJumps();
|
||||
@@ -144,8 +148,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
return false;
|
||||
}
|
||||
if (!mthInfo.isConstructor()) {
|
||||
LOG.warn("Wrong signature parse result: " + sp + " -> " + argsTypes
|
||||
+ ", not generic version: " + mthArgs);
|
||||
LOG.warn("Wrong signature parse result: {} -> {}, not generic version: {}", sp, argsTypes, mthArgs);
|
||||
return false;
|
||||
} else if (getParentClass().getAccessFlags().isEnum()) {
|
||||
// TODO:
|
||||
@@ -161,7 +164,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
initArguments(argsTypes);
|
||||
} catch (JadxRuntimeException e) {
|
||||
LOG.error("Method signature parse error: " + this, e);
|
||||
LOG.error("Method signature parse error: {}", this, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -180,7 +183,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
if (accFlags.isStatic()) {
|
||||
thisArg = null;
|
||||
} else {
|
||||
MthParameterArg arg = InsnArg.parameterReg(pos - 1, parentClass.getClassInfo().getType());
|
||||
TypeImmutableArg arg = InsnArg.typeImmutableReg(pos - 1, parentClass.getClassInfo().getType());
|
||||
arg.markAsThis();
|
||||
thisArg = arg;
|
||||
}
|
||||
@@ -190,7 +193,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
argsList = new ArrayList<RegisterArg>(args.size());
|
||||
for (ArgType arg : args) {
|
||||
argsList.add(InsnArg.parameterReg(pos, arg));
|
||||
argsList.add(InsnArg.typeImmutableReg(pos, arg));
|
||||
pos += arg.getRegCount();
|
||||
}
|
||||
}
|
||||
@@ -201,9 +204,8 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
list.add(thisArg);
|
||||
list.addAll(argsList);
|
||||
return list;
|
||||
} else {
|
||||
return argsList;
|
||||
}
|
||||
return argsList;
|
||||
}
|
||||
|
||||
public RegisterArg removeFirstArgument() {
|
||||
@@ -227,6 +229,9 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
InsnNode[] insnByOffset = instructions;
|
||||
CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
|
||||
Try[] tries = mthCode.getTries();
|
||||
if (catchBlocks.length == 0 && tries.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int hc = 0;
|
||||
Set<Integer> addrs = new HashSet<Integer>();
|
||||
@@ -271,7 +276,6 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
for (TryCatchBlock ct : catches) {
|
||||
for (ExceptionHandler eh : ct.getHandlers()) {
|
||||
int addr = eh.getHandleOffset();
|
||||
// assert addrs.add(addr) : "Instruction already contains EXC_HANDLER attribute";
|
||||
ExcHandlerAttr ehAttr = new ExcHandlerAttr(ct, eh);
|
||||
insnByOffset[addr].addAttr(ehAttr);
|
||||
}
|
||||
@@ -284,13 +288,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
int offset = aTry.getStartAddress();
|
||||
int end = offset + aTry.getInstructionCount() - 1;
|
||||
|
||||
insnByOffset[offset].add(AFlag.TRY_ENTER);
|
||||
InsnNode insn = insnByOffset[offset];
|
||||
insn.add(AFlag.TRY_ENTER);
|
||||
while (offset <= end && offset >= 0) {
|
||||
catchBlock.addInsn(insnByOffset[offset]);
|
||||
insn = insnByOffset[offset];
|
||||
catchBlock.addInsn(insn);
|
||||
offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||
}
|
||||
if (insnByOffset[end] != null) {
|
||||
insnByOffset[end].add(AFlag.TRY_LEAVE);
|
||||
} else {
|
||||
insn.add(AFlag.TRY_LEAVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,18 +311,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
continue;
|
||||
}
|
||||
switch (insn.getType()) {
|
||||
case SWITCH: {
|
||||
case SWITCH:
|
||||
SwitchNode sw = (SwitchNode) insn;
|
||||
for (int target : sw.getTargets()) {
|
||||
addJump(insnByOffset, offset, target);
|
||||
}
|
||||
// default case
|
||||
int next = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||
if (next != -1) {
|
||||
addJump(insnByOffset, offset, next);
|
||||
int nextInsnOffset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||
if (nextInsnOffset != -1) {
|
||||
addJump(insnByOffset, offset, nextInsnOffset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IF:
|
||||
int next = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||
@@ -339,12 +346,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String name = mthInfo.getName();
|
||||
if (name.equals(parentClass.getShortName())) {
|
||||
return name + "_";
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
return mthInfo.getName();
|
||||
}
|
||||
|
||||
public ClassNode getParentClass() {
|
||||
@@ -355,6 +357,10 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
return noCode;
|
||||
}
|
||||
|
||||
public int getCodeSize() {
|
||||
return codeSize;
|
||||
}
|
||||
|
||||
public InsnNode[] getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
@@ -404,10 +410,14 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
if (loops.isEmpty()) {
|
||||
loops = new ArrayList<LoopInfo>(5);
|
||||
}
|
||||
loop.setId(loops.size());
|
||||
loops.add(loop);
|
||||
}
|
||||
|
||||
public LoopInfo getLoopForBlock(BlockNode block) {
|
||||
if (loops.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (LoopInfo loop : loops) {
|
||||
if (loop.getLoopBlocks().contains(block)) {
|
||||
return loop;
|
||||
@@ -416,10 +426,27 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<LoopInfo> getAllLoopsForBlock(BlockNode block) {
|
||||
if (loops.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<LoopInfo> list = new ArrayList<LoopInfo>(loops.size());
|
||||
for (LoopInfo loop : loops) {
|
||||
if (loop.getLoopBlocks().contains(block)) {
|
||||
list.add(loop);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public int getLoopsCount() {
|
||||
return loops.size();
|
||||
}
|
||||
|
||||
public Iterable<LoopInfo> getLoops() {
|
||||
return loops;
|
||||
}
|
||||
|
||||
public ExceptionHandler addExceptionHandler(ExceptionHandler handler) {
|
||||
if (exceptionHandlers.isEmpty()) {
|
||||
exceptionHandlers = new ArrayList<ExceptionHandler>(2);
|
||||
@@ -467,6 +494,24 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDefaultConstructor() {
|
||||
boolean result = false;
|
||||
if (accFlags.isConstructor() && mthInfo.isConstructor()) {
|
||||
int defaultArgCount = 0;
|
||||
/** workaround for non-static inner class constructor, that has synthetic argument */
|
||||
if (parentClass.getClassInfo().isInner()
|
||||
&& !parentClass.getAccessFlags().isStatic()) {
|
||||
ClassNode outerCls = parentClass.getParentClass();
|
||||
if (argsList != null && !argsList.isEmpty()
|
||||
&& argsList.get(0).getType().equals(outerCls.getClassInfo().getType())) {
|
||||
defaultArgCount = 1;
|
||||
}
|
||||
}
|
||||
result = (argsList == null) || (argsList.size() == defaultArgCount);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getRegsCount() {
|
||||
return regsCount;
|
||||
}
|
||||
@@ -475,7 +520,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
||||
return debugInfoOffset;
|
||||
}
|
||||
|
||||
public SSAVar makeNewSVar(int regNum, int[] versions, RegisterArg arg) {
|
||||
public SSAVar makeNewSVar(int regNum, int[] versions, @NotNull RegisterArg arg) {
|
||||
SSAVar var = new SSAVar(regNum, versions[regNum], arg);
|
||||
versions[regNum]++;
|
||||
if (sVars.isEmpty()) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.core.dex.nodes.parser;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -112,8 +113,9 @@ public class DebugInfoParser {
|
||||
int regNum = section.readUleb128();
|
||||
LocalVar var = locals[regNum];
|
||||
if (var != null) {
|
||||
var.end(addr, line);
|
||||
setVar(var);
|
||||
if (var.end(addr, line)) {
|
||||
setVar(var);
|
||||
}
|
||||
var.start(addr, line);
|
||||
}
|
||||
break;
|
||||
@@ -160,7 +162,7 @@ public class DebugInfoParser {
|
||||
|
||||
for (LocalVar var : locals) {
|
||||
if (var != null && !var.isEnd()) {
|
||||
var.end(addr, line);
|
||||
var.end(mth.getCodeSize() - 1, line);
|
||||
setVar(var);
|
||||
}
|
||||
}
|
||||
@@ -169,6 +171,8 @@ public class DebugInfoParser {
|
||||
|
||||
private int addrChange(int addr, int addrInc, int line) {
|
||||
int newAddr = addr + addrInc;
|
||||
int maxAddr = insnByOffset.length - 1;
|
||||
newAddr = Math.min(newAddr, maxAddr);
|
||||
for (int i = addr + 1; i <= newAddr; i++) {
|
||||
InsnNode insn = insnByOffset[i];
|
||||
if (insn == null) {
|
||||
@@ -236,7 +240,27 @@ public class DebugInfoParser {
|
||||
if (arg != null && arg.isRegister()) {
|
||||
RegisterArg reg = (RegisterArg) arg;
|
||||
if (var.getRegNum() == reg.getRegNum()) {
|
||||
reg.mergeDebugInfo(var.getType(), var.getName());
|
||||
SSAVar ssaVar = reg.getSVar();
|
||||
|
||||
boolean mergeRequired = false;
|
||||
|
||||
if (ssaVar != null) {
|
||||
int ssaEnd = ssaVar.getEndAddr();
|
||||
int ssaStart = ssaVar.getStartAddr();
|
||||
int localStart = var.getStartAddr();
|
||||
int localEnd = var.getEndAddr();
|
||||
|
||||
boolean isIntersected = !((localEnd < ssaStart) || (ssaEnd < localStart));
|
||||
if (isIntersected && (ssaEnd <= localEnd)) {
|
||||
mergeRequired = true;
|
||||
}
|
||||
} else {
|
||||
mergeRequired = true;
|
||||
}
|
||||
|
||||
if (mergeRequired) {
|
||||
reg.mergeDebugInfo(var.getType(), var.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ final class LocalVar {
|
||||
|
||||
public LocalVar(DexNode dex, int rn, int nameId, int typeId, int signId) {
|
||||
this.regNum = rn;
|
||||
String name = (nameId == DexNode.NO_INDEX ? null : dex.getString(nameId));
|
||||
ArgType type = (typeId == DexNode.NO_INDEX ? null : dex.getType(typeId));
|
||||
String sign = (signId == DexNode.NO_INDEX ? null : dex.getString(signId));
|
||||
String name = nameId == DexNode.NO_INDEX ? null : dex.getString(nameId);
|
||||
ArgType type = typeId == DexNode.NO_INDEX ? null : dex.getType(typeId);
|
||||
String sign = signId == DexNode.NO_INDEX ? null : dex.getString(signId);
|
||||
|
||||
init(name, type, sign);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ final class LocalVar {
|
||||
type = gType;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Can't parse signature for local variable: " + sign, e);
|
||||
LOG.error("Can't parse signature for local variable: {}", sign, e);
|
||||
}
|
||||
}
|
||||
this.name = name;
|
||||
@@ -56,10 +56,8 @@ final class LocalVar {
|
||||
LOG.warn("Generic type in debug info not equals: {} != {}", type, gType);
|
||||
}
|
||||
apply = true;
|
||||
} else if (el.isGenericType()) {
|
||||
apply = true;
|
||||
} else {
|
||||
apply = false;
|
||||
apply = el.isGenericType();
|
||||
}
|
||||
return apply;
|
||||
}
|
||||
@@ -69,9 +67,20 @@ final class LocalVar {
|
||||
this.startAddr = addr;
|
||||
}
|
||||
|
||||
public void end(int addr, int line) {
|
||||
this.isEnd = true;
|
||||
this.endAddr = addr;
|
||||
/**
|
||||
* Sets end address of local variable
|
||||
*
|
||||
* @param addr address
|
||||
* @param line source line
|
||||
* @return <b>true</b> if local variable was active, else <b>false</b>
|
||||
*/
|
||||
public boolean end(int addr, int line) {
|
||||
if (!isEnd) {
|
||||
this.isEnd = true;
|
||||
this.endAddr = addr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getRegNum() {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class TernaryRegion extends AbstractRegion {
|
||||
private final IBlock container;
|
||||
|
||||
public TernaryRegion(IRegion parent, BlockNode block) {
|
||||
super(parent);
|
||||
this.container = block;
|
||||
}
|
||||
|
||||
public IBlock getBlock() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
return Collections.singletonList((IContainer) container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
return container.baseString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TERN:" + container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package jadx.core.dex.regions;
|
||||
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class TryCatchRegion extends AbstractRegion {
|
||||
|
||||
private final IContainer tryRegion;
|
||||
private List<IContainer> catchRegions = Collections.emptyList();
|
||||
private TryCatchBlock tryCatchBlock;
|
||||
|
||||
public TryCatchRegion(IRegion parent, IContainer tryRegion) {
|
||||
super(parent);
|
||||
this.tryRegion = tryRegion;
|
||||
}
|
||||
|
||||
public IContainer getTryRegion() {
|
||||
return tryRegion;
|
||||
}
|
||||
|
||||
public List<IContainer> getCatchRegions() {
|
||||
return catchRegions;
|
||||
}
|
||||
|
||||
public TryCatchBlock geTryCatchBlock() {
|
||||
return tryCatchBlock;
|
||||
}
|
||||
|
||||
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) {
|
||||
this.tryCatchBlock = tryCatchBlock;
|
||||
this.catchRegions = new ArrayList<IContainer>(tryCatchBlock.getHandlersCount());
|
||||
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
||||
catchRegions.add(handler.getHandlerRegion());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(1 + catchRegions.size());
|
||||
all.add(tryRegion);
|
||||
all.addAll(catchRegions);
|
||||
return Collections.unmodifiableList(all);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
return tryRegion.baseString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Try: " + tryRegion
|
||||
+ " catches: " + Utils.listToString(catchRegions);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.core.dex.regions;
|
||||
package jadx.core.dex.regions.conditions;
|
||||
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.IfOp;
|
||||
+19
-11
@@ -1,9 +1,8 @@
|
||||
package jadx.core.dex.regions;
|
||||
package jadx.core.dex.regions.conditions;
|
||||
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.IfOp;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -22,6 +21,7 @@ public final class IfCondition {
|
||||
|
||||
public enum Mode {
|
||||
COMPARE,
|
||||
TERNARY,
|
||||
NOT,
|
||||
AND,
|
||||
OR
|
||||
@@ -64,6 +64,10 @@ public final class IfCondition {
|
||||
return new IfCondition(new Compare(insn));
|
||||
}
|
||||
|
||||
public static IfCondition ternary(IfCondition a, IfCondition b, IfCondition c) {
|
||||
return new IfCondition(Mode.TERNARY, Arrays.asList(a, b, c));
|
||||
}
|
||||
|
||||
public static IfCondition merge(Mode mode, IfCondition a, IfCondition b) {
|
||||
if (a.getMode() == mode) {
|
||||
IfCondition n = new IfCondition(a);
|
||||
@@ -89,6 +93,10 @@ public final class IfCondition {
|
||||
return args.get(1);
|
||||
}
|
||||
|
||||
public IfCondition third() {
|
||||
return args.get(2);
|
||||
}
|
||||
|
||||
public void addArg(IfCondition c) {
|
||||
args.add(c);
|
||||
}
|
||||
@@ -106,6 +114,8 @@ public final class IfCondition {
|
||||
switch (mode) {
|
||||
case COMPARE:
|
||||
return new IfCondition(cond.getCompare().invert());
|
||||
case TERNARY:
|
||||
return ternary(not(cond.first()), cond.third(), cond.second());
|
||||
case NOT:
|
||||
return cond.first();
|
||||
case AND:
|
||||
@@ -154,7 +164,10 @@ public final class IfCondition {
|
||||
cond = new IfCondition(cond.getMode(), args);
|
||||
}
|
||||
if (cond.getMode() == Mode.NOT && cond.first().getMode() == Mode.NOT) {
|
||||
cond = cond.first().first();
|
||||
cond = invert(cond.first());
|
||||
}
|
||||
if (cond.getMode() == Mode.TERNARY && cond.first().getMode() == Mode.NOT) {
|
||||
cond = invert(cond);
|
||||
}
|
||||
|
||||
// for condition with a lot of negations => make invert
|
||||
@@ -195,14 +208,7 @@ public final class IfCondition {
|
||||
public List<RegisterArg> getRegisterArgs() {
|
||||
List<RegisterArg> list = new LinkedList<RegisterArg>();
|
||||
if (mode == Mode.COMPARE) {
|
||||
InsnArg a = compare.getA();
|
||||
if (a.isRegister()) {
|
||||
list.add((RegisterArg) a);
|
||||
}
|
||||
InsnArg b = compare.getB();
|
||||
if (b.isRegister()) {
|
||||
list.add((RegisterArg) b);
|
||||
}
|
||||
compare.getInsn().getRegisterArgs(list);
|
||||
} else {
|
||||
for (IfCondition arg : args) {
|
||||
list.addAll(arg.getRegisterArgs());
|
||||
@@ -216,6 +222,8 @@ public final class IfCondition {
|
||||
switch (mode) {
|
||||
case COMPARE:
|
||||
return compare.toString();
|
||||
case TERNARY:
|
||||
return first() + " ? " + second() + " : " + third();
|
||||
case NOT:
|
||||
return "!" + first();
|
||||
case AND:
|
||||
+32
-11
@@ -1,4 +1,4 @@
|
||||
package jadx.core.dex.regions;
|
||||
package jadx.core.dex.regions.conditions;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
|
||||
@@ -7,34 +7,51 @@ import java.util.Set;
|
||||
|
||||
public final class IfInfo {
|
||||
private final IfCondition condition;
|
||||
private final Set<BlockNode> mergedBlocks = new HashSet<BlockNode>();
|
||||
private final Set<BlockNode> mergedBlocks;
|
||||
private final BlockNode thenBlock;
|
||||
private final BlockNode elseBlock;
|
||||
private final Set<BlockNode> skipBlocks;
|
||||
private BlockNode outBlock;
|
||||
@Deprecated
|
||||
private BlockNode ifBlock;
|
||||
|
||||
public IfInfo(IfCondition condition, BlockNode thenBlock, BlockNode elseBlock) {
|
||||
this.condition = condition;
|
||||
this.thenBlock = thenBlock;
|
||||
this.elseBlock = elseBlock;
|
||||
this(condition, thenBlock, elseBlock, new HashSet<BlockNode>(), new HashSet<BlockNode>());
|
||||
}
|
||||
|
||||
public IfInfo(IfCondition condition, IfInfo info) {
|
||||
this(condition, info.getThenBlock(), info.getElseBlock(), info.getMergedBlocks(), info.getSkipBlocks());
|
||||
}
|
||||
|
||||
public IfInfo(IfInfo info, BlockNode thenBlock, BlockNode elseBlock) {
|
||||
this(info.getCondition(), thenBlock, elseBlock, info.getMergedBlocks(), info.getSkipBlocks());
|
||||
}
|
||||
|
||||
private IfInfo(IfCondition condition, BlockNode thenBlock, BlockNode elseBlock,
|
||||
Set<BlockNode> mergedBlocks, Set<BlockNode> skipBlocks) {
|
||||
this.condition = condition;
|
||||
this.thenBlock = info.getThenBlock();
|
||||
this.elseBlock = info.getElseBlock();
|
||||
this.mergedBlocks.addAll(info.getMergedBlocks());
|
||||
this.thenBlock = thenBlock;
|
||||
this.elseBlock = elseBlock;
|
||||
this.mergedBlocks = mergedBlocks;
|
||||
this.skipBlocks = skipBlocks;
|
||||
}
|
||||
|
||||
public static IfInfo invert(IfInfo info) {
|
||||
IfInfo tmpIf = new IfInfo(IfCondition.invert(info.getCondition()),
|
||||
info.getElseBlock(), info.getThenBlock());
|
||||
IfCondition invertedCondition = IfCondition.invert(info.getCondition());
|
||||
IfInfo tmpIf = new IfInfo(invertedCondition,
|
||||
info.getElseBlock(), info.getThenBlock(),
|
||||
info.getMergedBlocks(), info.getSkipBlocks());
|
||||
tmpIf.setIfBlock(info.getIfBlock());
|
||||
tmpIf.getMergedBlocks().addAll(info.getMergedBlocks());
|
||||
return tmpIf;
|
||||
}
|
||||
|
||||
public void merge(IfInfo... arr) {
|
||||
for (IfInfo info : arr) {
|
||||
mergedBlocks.addAll(info.getMergedBlocks());
|
||||
skipBlocks.addAll(info.getSkipBlocks());
|
||||
}
|
||||
}
|
||||
|
||||
public IfCondition getCondition() {
|
||||
return condition;
|
||||
}
|
||||
@@ -43,6 +60,10 @@ public final class IfInfo {
|
||||
return mergedBlocks;
|
||||
}
|
||||
|
||||
public Set<BlockNode> getSkipBlocks() {
|
||||
return skipBlocks;
|
||||
}
|
||||
|
||||
public BlockNode getThenBlock() {
|
||||
return thenBlock;
|
||||
}
|
||||
+3
-21
@@ -1,8 +1,9 @@
|
||||
package jadx.core.dex.regions;
|
||||
package jadx.core.dex.regions.conditions;
|
||||
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.regions.AbstractRegion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -16,8 +17,6 @@ public final class IfRegion extends AbstractRegion {
|
||||
private IContainer thenRegion;
|
||||
private IContainer elseRegion;
|
||||
|
||||
private TernaryRegion ternRegion;
|
||||
|
||||
public IfRegion(IRegion parent, BlockNode header) {
|
||||
super(parent);
|
||||
assert header.getInstructions().size() == 1;
|
||||
@@ -53,14 +52,6 @@ public final class IfRegion extends AbstractRegion {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setTernRegion(TernaryRegion ternRegion) {
|
||||
this.ternRegion = ternRegion;
|
||||
}
|
||||
|
||||
public TernaryRegion getTernRegion() {
|
||||
return ternRegion;
|
||||
}
|
||||
|
||||
public boolean simplifyCondition() {
|
||||
IfCondition cond = IfCondition.simplify(condition);
|
||||
if (cond != condition) {
|
||||
@@ -87,10 +78,7 @@ public final class IfRegion extends AbstractRegion {
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
if (ternRegion != null) {
|
||||
return ternRegion.getSubBlocks();
|
||||
}
|
||||
ArrayList<IContainer> all = new ArrayList<IContainer>(3);
|
||||
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||
all.add(header);
|
||||
if (thenRegion != null) {
|
||||
all.add(thenRegion);
|
||||
@@ -116,9 +104,6 @@ public final class IfRegion extends AbstractRegion {
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
if (ternRegion != null) {
|
||||
return ternRegion.baseString();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (thenRegion != null) {
|
||||
sb.append(thenRegion.baseString());
|
||||
@@ -131,9 +116,6 @@ public final class IfRegion extends AbstractRegion {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (ternRegion != null) {
|
||||
return ternRegion.toString();
|
||||
}
|
||||
return "IF(" + condition + ") then " + thenRegion + " else " + elseRegion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package jadx.core.dex.regions.loops;
|
||||
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
|
||||
public final class ForEachLoop extends LoopType {
|
||||
private final RegisterArg varArg;
|
||||
private final InsnArg iterableArg;
|
||||
|
||||
public ForEachLoop(RegisterArg varArg, InsnArg iterableArg) {
|
||||
this.varArg = varArg;
|
||||
this.iterableArg = iterableArg;
|
||||
}
|
||||
|
||||
public RegisterArg getVarArg() {
|
||||
return varArg;
|
||||
}
|
||||
|
||||
public InsnArg getIterableArg() {
|
||||
return iterableArg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package jadx.core.dex.regions.loops;
|
||||
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public final class ForLoop extends LoopType {
|
||||
|
||||
private final InsnNode initInsn;
|
||||
private final InsnNode incrInsn;
|
||||
|
||||
public ForLoop(InsnNode initInsn, InsnNode incrInsn) {
|
||||
this.initInsn = initInsn;
|
||||
this.incrInsn = incrInsn;
|
||||
}
|
||||
|
||||
public InsnNode getInitInsn() {
|
||||
return initInsn;
|
||||
}
|
||||
|
||||
public InsnNode getIncrInsn() {
|
||||
return incrInsn;
|
||||
}
|
||||
}
|
||||
+46
-23
@@ -1,11 +1,14 @@
|
||||
package jadx.core.dex.regions;
|
||||
package jadx.core.dex.regions.loops;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.AbstractRegion;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -13,21 +16,29 @@ import java.util.List;
|
||||
|
||||
public final class LoopRegion extends AbstractRegion {
|
||||
|
||||
private final LoopInfo info;
|
||||
// loop header contains one 'if' insn, equals null for infinite loop
|
||||
private IfCondition condition;
|
||||
private final BlockNode conditionBlock;
|
||||
// instruction which must be executed before condition in every loop
|
||||
private BlockNode preCondition;
|
||||
private IContainer body;
|
||||
private IRegion body;
|
||||
private final boolean conditionAtEnd;
|
||||
|
||||
public LoopRegion(IRegion parent, BlockNode header, boolean reversed) {
|
||||
private LoopType type;
|
||||
|
||||
public LoopRegion(IRegion parent, LoopInfo info, BlockNode header, boolean reversed) {
|
||||
super(parent);
|
||||
this.info = info;
|
||||
this.conditionBlock = header;
|
||||
this.condition = IfCondition.fromIfBlock(header);
|
||||
this.conditionAtEnd = reversed;
|
||||
}
|
||||
|
||||
public LoopInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public IfCondition getCondition() {
|
||||
return condition;
|
||||
}
|
||||
@@ -40,11 +51,11 @@ public final class LoopRegion extends AbstractRegion {
|
||||
return conditionBlock;
|
||||
}
|
||||
|
||||
public IContainer getBody() {
|
||||
public IRegion getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(IContainer body) {
|
||||
public void setBody(IRegion body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@@ -77,25 +88,24 @@ public final class LoopRegion extends AbstractRegion {
|
||||
InsnNode insn = insns.get(i);
|
||||
if (insn.getResult() == null) {
|
||||
return false;
|
||||
} else {
|
||||
RegisterArg res = insn.getResult();
|
||||
if (res.getSVar().getUseCount() > 1) {
|
||||
return false;
|
||||
}
|
||||
boolean found = false;
|
||||
// search result arg in other insns
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
if (insns.get(i).containsArg(res)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
// or in if insn
|
||||
if (!found && ifInsn.containsArg(res)) {
|
||||
}
|
||||
RegisterArg res = insn.getResult();
|
||||
if (res.getSVar().getUseCount() > 1) {
|
||||
return false;
|
||||
}
|
||||
boolean found = false;
|
||||
// search result arg in other insns
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
if (insns.get(i).containsArg(res)) {
|
||||
found = true;
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// or in if insn
|
||||
if (!found && ifInsn.containsArg(res)) {
|
||||
found = true;
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -116,6 +126,14 @@ public final class LoopRegion extends AbstractRegion {
|
||||
}
|
||||
}
|
||||
|
||||
public LoopType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(LoopType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IContainer> getSubBlocks() {
|
||||
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||
@@ -131,6 +149,11 @@ public final class LoopRegion extends AbstractRegion {
|
||||
return Collections.unmodifiableList(all);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
return body.baseString();
|
||||
@@ -138,6 +161,6 @@ public final class LoopRegion extends AbstractRegion {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOOP: " + baseString();
|
||||
return "LOOP:" + info.getId() + ": " + baseString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package jadx.core.dex.regions.loops;
|
||||
|
||||
public abstract class LoopType {
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package jadx.core.dex.trycatch;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
@@ -18,7 +18,7 @@ public class ExceptionHandler {
|
||||
private BlockNode handlerBlock;
|
||||
private final List<BlockNode> blocks = new ArrayList<BlockNode>();
|
||||
private IContainer handlerRegion;
|
||||
private NamedArg arg;
|
||||
private InsnArg arg;
|
||||
|
||||
private TryCatchBlock tryBlock;
|
||||
|
||||
@@ -63,11 +63,11 @@ public class ExceptionHandler {
|
||||
this.handlerRegion = handlerRegion;
|
||||
}
|
||||
|
||||
public NamedArg getArg() {
|
||||
public InsnArg getArg() {
|
||||
return arg;
|
||||
}
|
||||
|
||||
public void setArg(NamedArg arg) {
|
||||
public void setArg(InsnArg arg) {
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.EmptyBitSet;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -28,6 +27,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static jadx.core.utils.EmptyBitSet.EMPTY;
|
||||
|
||||
public class BlockMakerVisitor extends AbstractVisitor {
|
||||
|
||||
// leave these instructions alone in block node
|
||||
@@ -36,9 +37,8 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
InsnType.IF,
|
||||
InsnType.SWITCH,
|
||||
InsnType.MONITOR_ENTER,
|
||||
InsnType.MONITOR_EXIT);
|
||||
|
||||
private static final BitSet EMPTY_BITSET = new EmptyBitSet();
|
||||
InsnType.MONITOR_EXIT
|
||||
);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
@@ -103,7 +103,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
// add this insn in new block
|
||||
block = startNewBlock(mth, -1);
|
||||
curBlock.add(AFlag.SYNTHETIC);
|
||||
block.addAttr(new SplitterBlockAttr(curBlock));
|
||||
SplitterBlockAttr splitter = new SplitterBlockAttr(curBlock);
|
||||
block.addAttr(splitter);
|
||||
curBlock.addAttr(splitter);
|
||||
connect(curBlock, block);
|
||||
curBlock = block;
|
||||
} else {
|
||||
@@ -131,12 +133,16 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
// get synthetic block for handlers
|
||||
SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK);
|
||||
if (catches != null && spl != null) {
|
||||
BlockNode connBlock = spl.getBlock();
|
||||
BlockNode splitterBlock = spl.getBlock();
|
||||
boolean tryEnd = insn.contains(AFlag.TRY_LEAVE);
|
||||
for (ExceptionHandler h : catches.getTryBlock().getHandlers()) {
|
||||
BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap);
|
||||
BlockNode handlerBlock = getBlock(h.getHandleOffset(), blocksMap);
|
||||
// skip self loop in handler
|
||||
if (connBlock != destBlock) {
|
||||
connect(connBlock, destBlock);
|
||||
if (splitterBlock != handlerBlock) {
|
||||
connect(splitterBlock, handlerBlock);
|
||||
}
|
||||
if (tryEnd) {
|
||||
connect(block, handlerBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +196,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
}
|
||||
computeDominanceFrontier(mth);
|
||||
registerLoops(mth);
|
||||
processNestedLoops(mth);
|
||||
}
|
||||
|
||||
private static BlockNode getBlock(int offset, Map<Integer, BlockNode> blocksMap) {
|
||||
@@ -291,7 +298,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
|
||||
private static void computeDominanceFrontier(MethodNode mth) {
|
||||
for (BlockNode exit : mth.getExitBlocks()) {
|
||||
exit.setDomFrontier(EMPTY_BITSET);
|
||||
exit.setDomFrontier(EMPTY);
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
computeBlockDF(mth, block);
|
||||
@@ -323,7 +330,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
if (domFrontier == null || domFrontier.cardinality() == 0) {
|
||||
domFrontier = EMPTY_BITSET;
|
||||
domFrontier = EMPTY;
|
||||
}
|
||||
block.setDomFrontier(domFrontier);
|
||||
}
|
||||
@@ -357,15 +364,40 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
|
||||
private static void registerLoops(MethodNode mth) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
List<LoopInfo> loops = block.getAll(AType.LOOP);
|
||||
if (block.contains(AFlag.LOOP_START)) {
|
||||
for (LoopInfo loop : loops) {
|
||||
for (LoopInfo loop : block.getAll(AType.LOOP)) {
|
||||
mth.registerLoop(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processNestedLoops(MethodNode mth) {
|
||||
if (mth.getLoopsCount() == 0) {
|
||||
return;
|
||||
}
|
||||
for (LoopInfo outLoop : mth.getLoops()) {
|
||||
for (LoopInfo innerLoop : mth.getLoops()) {
|
||||
if (outLoop == innerLoop) {
|
||||
continue;
|
||||
}
|
||||
if (outLoop.getLoopBlocks().containsAll(innerLoop.getLoopBlocks())) {
|
||||
LoopInfo parentLoop = innerLoop.getParentLoop();
|
||||
if (parentLoop != null) {
|
||||
if (parentLoop.getLoopBlocks().containsAll(outLoop.getLoopBlocks())) {
|
||||
outLoop.setParentLoop(parentLoop);
|
||||
innerLoop.setParentLoop(outLoop);
|
||||
} else {
|
||||
parentLoop.setParentLoop(outLoop);
|
||||
}
|
||||
} else {
|
||||
innerLoop.setParentLoop(outLoop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean modifyBlocksTree(MethodNode mth) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
if (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {
|
||||
@@ -395,9 +427,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// insert additional blocks if loop has several exits
|
||||
if (loops.size() == 1) {
|
||||
LoopInfo loop = loops.get(0);
|
||||
// insert additional blocks for possible 'break' insertion
|
||||
List<Edge> edges = loop.getExitEdges();
|
||||
if (!edges.isEmpty()) {
|
||||
boolean change = false;
|
||||
@@ -412,6 +444,21 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// insert additional blocks for possible 'continue' insertion
|
||||
BlockNode loopEnd = loop.getEnd();
|
||||
if (loopEnd.getPredecessors().size() > 1) {
|
||||
boolean change = false;
|
||||
List<BlockNode> nodes = new ArrayList<BlockNode>(loopEnd.getPredecessors());
|
||||
for (BlockNode pred : nodes) {
|
||||
if (!pred.contains(AFlag.SYNTHETIC)) {
|
||||
insertBlockBetween(mth, pred, loopEnd);
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return splitReturn(mth);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
@@ -43,30 +44,25 @@ public class BlockProcessingHelper {
|
||||
* Set exception handler attribute for whole block
|
||||
*/
|
||||
private static void markExceptionHandlers(BlockNode block) {
|
||||
if (!block.getInstructions().isEmpty()) {
|
||||
InsnNode me = block.getInstructions().get(0);
|
||||
ExcHandlerAttr handlerAttr = me.get(AType.EXC_HANDLER);
|
||||
if (handlerAttr != null && me.getType() == InsnType.MOVE_EXCEPTION) {
|
||||
ExceptionHandler excHandler = handlerAttr.getHandler();
|
||||
assert me.getOffset() == excHandler.getHandleOffset();
|
||||
// set correct type for 'move-exception' operation
|
||||
RegisterArg resArg = me.getResult();
|
||||
NamedArg excArg = (NamedArg) me.getArg(0);
|
||||
ArgType type;
|
||||
if (excHandler.isCatchAll()) {
|
||||
type = ArgType.THROWABLE;
|
||||
excArg.setName("th");
|
||||
} else {
|
||||
type = excHandler.getCatchType().getType();
|
||||
excArg.setName("e");
|
||||
}
|
||||
resArg.forceType(type);
|
||||
excArg.setType(type);
|
||||
|
||||
excHandler.setArg(excArg);
|
||||
block.addAttr(handlerAttr);
|
||||
}
|
||||
if (block.getInstructions().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
InsnNode me = block.getInstructions().get(0);
|
||||
ExcHandlerAttr handlerAttr = me.get(AType.EXC_HANDLER);
|
||||
if (handlerAttr == null || me.getType() != InsnType.MOVE_EXCEPTION) {
|
||||
return;
|
||||
}
|
||||
ExceptionHandler excHandler = handlerAttr.getHandler();
|
||||
block.addAttr(handlerAttr);
|
||||
// set correct type for 'move-exception' operation
|
||||
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
|
||||
|
||||
RegisterArg resArg = me.getResult();
|
||||
resArg = InsnArg.reg(resArg.getRegNum(), type);
|
||||
me.setResult(resArg);
|
||||
me.add(AFlag.DONT_INLINE);
|
||||
|
||||
excHandler.setArg(resArg);
|
||||
}
|
||||
|
||||
private static void processExceptionHandlers(MethodNode mth, BlockNode block) {
|
||||
|
||||
@@ -125,7 +125,7 @@ public class ClassModifier extends AbstractVisitor {
|
||||
List<InsnNode> insns = mth.getBasicBlocks().get(0).getInstructions();
|
||||
if (insns.size() == 1 && insns.get(0).getType() == InsnType.CONSTRUCTOR) {
|
||||
ConstructorInsn constr = (ConstructorInsn) insns.get(0);
|
||||
if (constr.isThis() && mth.getArguments(false).size() >= 1) {
|
||||
if (constr.isThis() && !mth.getArguments(false).isEmpty()) {
|
||||
mth.removeFirstArgument();
|
||||
mth.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class ClassModifier extends AbstractVisitor {
|
||||
|
||||
private static boolean allBlocksEmpty(List<BlockNode> blocks) {
|
||||
for (BlockNode block : blocks) {
|
||||
if (block.getInstructions().size() != 0) {
|
||||
if (!block.getInstructions().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.EmptyBitSet;
|
||||
import jadx.core.utils.InsnList;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@@ -55,7 +56,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
|
||||
public static List<RegisterArg> getArgs(InsnNode insn) {
|
||||
LinkedList<RegisterArg> args = new LinkedList<RegisterArg>();
|
||||
List<RegisterArg> args = new LinkedList<RegisterArg>();
|
||||
addArgs(insn, args);
|
||||
return args;
|
||||
}
|
||||
@@ -96,7 +97,8 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private boolean canMove(int from, int to) {
|
||||
List<RegisterArg> movedArgs = argsList.get(from).getArgs();
|
||||
ArgsInfo startInfo = argsList.get(from);
|
||||
List<RegisterArg> movedArgs = startInfo.getArgs();
|
||||
int start = from + 1;
|
||||
if (start == to) {
|
||||
// previous instruction or on edge of inline border
|
||||
@@ -105,9 +107,17 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
if (start > to) {
|
||||
throw new JadxRuntimeException("Invalid inline insn positions: " + start + " - " + to);
|
||||
}
|
||||
BitSet args = new BitSet();
|
||||
for (RegisterArg arg : movedArgs) {
|
||||
args.set(arg.getRegNum());
|
||||
BitSet movedSet;
|
||||
if (movedArgs.isEmpty()) {
|
||||
if (startInfo.insn.isConstInsn()) {
|
||||
return true;
|
||||
}
|
||||
movedSet = EmptyBitSet.EMPTY;
|
||||
} else {
|
||||
movedSet = new BitSet();
|
||||
for (RegisterArg arg : movedArgs) {
|
||||
movedSet.set(arg.getRegNum());
|
||||
}
|
||||
}
|
||||
for (int i = start; i < to; i++) {
|
||||
ArgsInfo argsInfo = argsList.get(i);
|
||||
@@ -115,7 +125,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
continue;
|
||||
}
|
||||
InsnNode curInsn = argsInfo.insn;
|
||||
if (!curInsn.canReorder() || usedArgAssign(curInsn, args)) {
|
||||
if (!curInsn.canReorder() || usedArgAssign(curInsn, movedSet)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -187,21 +197,22 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
List<WrapInfo> wrapList = new ArrayList<WrapInfo>();
|
||||
for (ArgsInfo argsInfo : argsList) {
|
||||
List<RegisterArg> args = argsInfo.getArgs();
|
||||
for (ListIterator<RegisterArg> it = args.listIterator(args.size()); it.hasPrevious(); ) {
|
||||
if (args.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
ListIterator<RegisterArg> it = args.listIterator(args.size());
|
||||
while (it.hasPrevious()) {
|
||||
RegisterArg arg = it.previous();
|
||||
// if (arg.getName() != null) {
|
||||
// continue;
|
||||
// }
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar.getAssign() == null) {
|
||||
continue;
|
||||
}
|
||||
// allow inline only one use arg or 'this'
|
||||
if (sVar.getVariableUseCount() != 1 && !arg.isThis()) {
|
||||
continue;
|
||||
}
|
||||
InsnNode assignInsn = sVar.getAssign().getParentInsn();
|
||||
if (assignInsn == null) {
|
||||
if (assignInsn == null || assignInsn.contains(AFlag.DONT_INLINE)) {
|
||||
continue;
|
||||
}
|
||||
int assignPos = insnList.getIndex(assignInsn);
|
||||
@@ -214,6 +225,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
// another block
|
||||
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);
|
||||
if (assignBlock != null
|
||||
&& assignInsn != arg.getParentInsn()
|
||||
&& canMoveBetweenBlocks(assignInsn, assignBlock, block, argsInfo.getInsn())) {
|
||||
arg.wrapInstruction(assignInsn);
|
||||
InsnList.remove(assignBlock, assignInsn);
|
||||
@@ -232,7 +244,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static boolean canMoveBetweenBlocks(InsnNode assignInsn, BlockNode assignBlock,
|
||||
BlockNode useBlock, InsnNode useInsn) {
|
||||
BlockNode useBlock, InsnNode useInsn) {
|
||||
if (!BlockUtils.isPathExists(assignBlock, useBlock)) {
|
||||
return false;
|
||||
}
|
||||
@@ -292,7 +304,7 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
// remove method args
|
||||
if (list.size() != 0 && args.size() != 0) {
|
||||
if (!list.isEmpty() && !args.isEmpty()) {
|
||||
list.removeAll(args);
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.InvokeType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -12,7 +14,6 @@ import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
toRemove.clear();
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (checkInsn(mth, block, insn)) {
|
||||
if (checkInsn(mth, insn)) {
|
||||
toRemove.add(insn);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +41,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkInsn(MethodNode mth, BlockNode block, InsnNode insn) {
|
||||
private static boolean checkInsn(MethodNode mth, InsnNode insn) {
|
||||
if (insn.getType() != InsnType.CONST) {
|
||||
return false;
|
||||
}
|
||||
@@ -48,28 +49,52 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
if (!arg.isLiteral()) {
|
||||
return false;
|
||||
}
|
||||
long lit = ((LiteralArg) arg).getLiteral();
|
||||
|
||||
SSAVar sVar = insn.getResult().getSVar();
|
||||
if (mth.getExceptionHandlersCount() != 0) {
|
||||
for (RegisterArg useArg : sVar.getUseList()) {
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
if (parentInsn != null) {
|
||||
// TODO: speed up expensive operations
|
||||
BlockNode useBlock = BlockUtils.getBlockByInsn(mth, parentInsn);
|
||||
if (!BlockUtils.isCleanPathExists(block, useBlock)) {
|
||||
return false;
|
||||
}
|
||||
if (lit == 0) {
|
||||
if (checkObjectInline(sVar)) {
|
||||
if (sVar.getUseCount() == 1) {
|
||||
insn.getResult().getAssignInsn().add(AFlag.DONT_INLINE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
ArgType resType = insn.getResult().getType();
|
||||
// make sure arg has correct type
|
||||
if (!arg.getType().isTypeKnown()) {
|
||||
arg.merge(resType);
|
||||
}
|
||||
long lit = ((LiteralArg) arg).getLiteral();
|
||||
return replaceConst(mth, sVar, lit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't inline null object if:
|
||||
* - used as instance arg in invoke instruction
|
||||
* - used in 'array.length'
|
||||
*/
|
||||
private static boolean checkObjectInline(SSAVar sVar) {
|
||||
for (RegisterArg useArg : sVar.getUseList()) {
|
||||
InsnNode insn = useArg.getParentInsn();
|
||||
if (insn != null) {
|
||||
InsnType insnType = insn.getType();
|
||||
if (insnType == InsnType.INVOKE) {
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
if (inv.getInvokeType() != InvokeType.STATIC
|
||||
&& inv.getArg(0) == useArg) {
|
||||
return true;
|
||||
}
|
||||
} else if (insnType == InsnType.ARRAY_LENGTH) {
|
||||
if (insn.getArg(0) == useArg) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean replaceConst(MethodNode mth, SSAVar sVar, long literal) {
|
||||
List<RegisterArg> use = new ArrayList<RegisterArg>(sVar.getUseList());
|
||||
int replaceCount = 0;
|
||||
@@ -85,6 +110,10 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
if (use.size() == 1 || arg.isTypeImmutable()) {
|
||||
// arg used only in one place
|
||||
litArg = InsnArg.lit(literal, arg.getType());
|
||||
} else if (useInsn.getType() == InsnType.MOVE
|
||||
&& !useInsn.getResult().getType().isTypeKnown()) {
|
||||
// save type for 'move' instructions (hard to find type in chains of 'move')
|
||||
litArg = InsnArg.lit(literal, arg.getType());
|
||||
} else {
|
||||
// in most cases type not equal arg.getType()
|
||||
// just set unknown type and run type fixer
|
||||
@@ -150,7 +179,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
List<ArgType> types = inv.getCallMth().getArgumentsTypes();
|
||||
int count = insn.getArgsCount();
|
||||
int k = (types.size() == count ? 0 : -1);
|
||||
int k = types.size() == count ? 0 : -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
if (!arg.getType().isTypeKnown()) {
|
||||
|
||||
@@ -30,16 +30,28 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
private final boolean useRegions;
|
||||
private final boolean rawInsn;
|
||||
|
||||
public DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
|
||||
public static DotGraphVisitor dump(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRaw(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, true);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRawRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, true);
|
||||
}
|
||||
|
||||
private DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
|
||||
this.dir = outDir;
|
||||
this.useRegions = useRegions;
|
||||
this.rawInsn = rawInsn;
|
||||
}
|
||||
|
||||
public DotGraphVisitor(File outDir, boolean useRegions) {
|
||||
this(outDir, useRegions, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
if (mth.isNoCode()) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -38,13 +39,12 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
|
||||
// collect enum fields, remove synthetic
|
||||
List<FieldNode> enumFields = new ArrayList<FieldNode>();
|
||||
for (Iterator<FieldNode> it = cls.getFields().iterator(); it.hasNext(); ) {
|
||||
FieldNode f = it.next();
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (f.getAccessFlags().isEnum()) {
|
||||
enumFields.add(f);
|
||||
it.remove();
|
||||
f.add(AFlag.DONT_GENERATE);
|
||||
} else if (f.getAccessFlags().isSynthetic()) {
|
||||
it.remove();
|
||||
f.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,8 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
cls.addAttr(attr);
|
||||
|
||||
if (staticMethod == null) {
|
||||
LOG.warn("Enum class init method not found: {}", cls);
|
||||
ErrorsCounter.classError(cls, "Enum class init method not found");
|
||||
// for this broken enum puts found fields and mark as inconsistent
|
||||
cls.add(AFlag.INCONSISTENT_CODE);
|
||||
for (FieldNode field : enumFields) {
|
||||
attr.getFields().add(new EnumField(field.getName(), 0));
|
||||
}
|
||||
@@ -115,21 +114,17 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
for (InsnNode insn : insns) {
|
||||
if (insn.getType() == InsnType.CONSTRUCTOR) {
|
||||
ConstructorInsn co = (ConstructorInsn) insn;
|
||||
|
||||
if (insn.getArgsCount() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassInfo clsInfo = co.getClassType();
|
||||
ClassNode constrCls = cls.dex().resolveClass(clsInfo);
|
||||
if (constrCls == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!clsInfo.equals(cls.getClassInfo()) && !constrCls.getAccessFlags().isEnum()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterArg nameArg = (RegisterArg) insn.getArg(0);
|
||||
// InsnArg pos = insn.getArg(1);
|
||||
// TODO add check: pos == j
|
||||
@@ -137,7 +132,6 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
if (name == null) {
|
||||
throw new JadxException("Unknown enum field name: " + cls);
|
||||
}
|
||||
|
||||
EnumField field = new EnumField(name, insn.getArgsCount() - 2);
|
||||
attr.getFields().add(field);
|
||||
for (int i = 2; i < insn.getArgsCount(); i++) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package jadx.core.dex.visitors;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -34,10 +33,8 @@ public class MethodInlineVisitor extends AbstractVisitor {
|
||||
// synthetic field getter
|
||||
BlockNode block = mth.getBasicBlocks().get(1);
|
||||
InsnNode insn = block.getInstructions().get(0);
|
||||
InsnNode inl = new InsnNode(InsnType.ARGS, 1);
|
||||
// set arg from 'return' instruction
|
||||
inl.addArg(insn.getArg(0));
|
||||
addInlineAttr(mth, inl);
|
||||
addInlineAttr(mth, InsnNode.wrapArg(insn.getArg(0)));
|
||||
} else {
|
||||
// synthetic field setter or method invoke
|
||||
if (firstBlock.getInstructions().size() == 1) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
@@ -13,7 +14,9 @@ import jadx.core.dex.instructions.SwitchNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -24,6 +27,7 @@ import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -41,8 +45,10 @@ public class ModVisitor extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
removeStep(mth);
|
||||
replaceStep(mth);
|
||||
|
||||
InstructionRemover remover = new InstructionRemover(mth);
|
||||
replaceStep(mth, remover);
|
||||
removeStep(mth, remover);
|
||||
|
||||
checkArgsNames(mth);
|
||||
|
||||
@@ -51,49 +57,16 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceStep(MethodNode mth) {
|
||||
private static void replaceStep(MethodNode mth, InstructionRemover remover) {
|
||||
ClassNode parentClass = mth.getParentClass();
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
InstructionRemover remover = new InstructionRemover(mth, block);
|
||||
|
||||
remover.setBlock(block);
|
||||
int size = block.getInstructions().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
InsnNode insn = block.getInstructions().get(i);
|
||||
switch (insn.getType()) {
|
||||
case INVOKE:
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
MethodInfo callMth = inv.getCallMth();
|
||||
if (callMth.isConstructor()) {
|
||||
ConstructorInsn co = new ConstructorInsn(mth, inv);
|
||||
removeInsnForArg(remover, co.getInstanceArg());
|
||||
boolean remove = false;
|
||||
if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) {
|
||||
remove = true;
|
||||
} else if (co.isThis() && co.getArgsCount() == 0) {
|
||||
MethodNode defCo = mth.getParentClass().searchMethodByName(callMth.getShortId());
|
||||
if (defCo == null || defCo.isNoCode()) {
|
||||
// default constructor not implemented
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
if (remove) {
|
||||
remover.add(insn);
|
||||
} else {
|
||||
replaceInsn(block, i, co);
|
||||
}
|
||||
} else {
|
||||
if (inv.getArgsCount() > 0) {
|
||||
for (int j = 0; j < inv.getArgsCount(); j++) {
|
||||
InsnArg arg = inv.getArg(j);
|
||||
if (arg.isLiteral()) {
|
||||
FieldNode f = parentClass.getConstFieldByLiteralArg((LiteralArg) arg);
|
||||
if (f != null) {
|
||||
arg.wrapInstruction(new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
processInvoke(mth, block, i, remover);
|
||||
break;
|
||||
|
||||
case CONST:
|
||||
@@ -145,17 +118,134 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void processInvoke(MethodNode mth, BlockNode block, int insnNumber, InstructionRemover remover) {
|
||||
ClassNode parentClass = mth.getParentClass();
|
||||
InsnNode insn = block.getInstructions().get(insnNumber);
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
MethodInfo callMth = inv.getCallMth();
|
||||
if (callMth.isConstructor()) {
|
||||
InsnNode instArgAssignInsn = ((RegisterArg) inv.getArg(0)).getAssignInsn();
|
||||
ConstructorInsn co = new ConstructorInsn(mth, inv);
|
||||
boolean remove = false;
|
||||
if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) {
|
||||
remove = true;
|
||||
} else if (co.isThis() && co.getArgsCount() == 0) {
|
||||
MethodNode defCo = parentClass.searchMethodByName(callMth.getShortId());
|
||||
if (defCo == null || defCo.isNoCode()) {
|
||||
// default constructor not implemented
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
// remove super() call in instance initializer
|
||||
if (parentClass.isAnonymous() && mth.isDefaultConstructor() && co.isSuper()) {
|
||||
remove = true;
|
||||
}
|
||||
if (remove) {
|
||||
remover.add(insn);
|
||||
} else {
|
||||
replaceInsn(block, insnNumber, co);
|
||||
if (co.isNewInstance()) {
|
||||
InsnNode newInstInsn = removeAssignChain(instArgAssignInsn, remover, InsnType.NEW_INSTANCE);
|
||||
if (newInstInsn != null) {
|
||||
RegisterArg instArg = newInstInsn.getResult();
|
||||
RegisterArg resultArg = co.getResult();
|
||||
if (!resultArg.equals(instArg)) {
|
||||
// replace all usages of 'instArg' with result of this constructor instruction
|
||||
for (RegisterArg useArg : new ArrayList<RegisterArg>(instArg.getSVar().getUseList())) {
|
||||
RegisterArg dup = resultArg.duplicate();
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
parentInsn.replaceArg(useArg, dup);
|
||||
dup.setParentInsn(parentInsn);
|
||||
resultArg.getSVar().use(dup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstructorInsn replace = processConstructor(mth, co);
|
||||
if (replace != null) {
|
||||
replaceInsn(block, insnNumber, replace);
|
||||
}
|
||||
}
|
||||
} else if (inv.getArgsCount() > 0) {
|
||||
for (int j = 0; j < inv.getArgsCount(); j++) {
|
||||
InsnArg arg = inv.getArg(j);
|
||||
if (arg.isLiteral()) {
|
||||
FieldNode f = parentClass.getConstFieldByLiteralArg((LiteralArg) arg);
|
||||
if (f != null) {
|
||||
arg.wrapInstruction(new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace call of synthetic constructor
|
||||
*/
|
||||
private static ConstructorInsn processConstructor(MethodNode mth, ConstructorInsn co) {
|
||||
MethodNode callMth = mth.dex().resolveMethod(co.getCallMth());
|
||||
if (callMth == null
|
||||
|| !callMth.getAccessFlags().isSynthetic()
|
||||
|| !allArgsNull(co)) {
|
||||
return null;
|
||||
}
|
||||
ClassNode classNode = mth.dex().resolveClass(callMth.getParentClass().getClassInfo());
|
||||
if (classNode == null) {
|
||||
return null;
|
||||
}
|
||||
boolean passThis = co.getArgsCount() >= 1 && co.getArg(0).isThis();
|
||||
String ctrId = "<init>(" + (passThis ? TypeGen.signature(co.getArg(0).getType()) : "") + ")V";
|
||||
MethodNode defCtr = classNode.searchMethodByName(ctrId);
|
||||
if (defCtr == null) {
|
||||
return null;
|
||||
}
|
||||
ConstructorInsn newInsn = new ConstructorInsn(defCtr.getMethodInfo(), co.getCallType(), co.getInstanceArg());
|
||||
newInsn.setResult(co.getResult());
|
||||
return newInsn;
|
||||
}
|
||||
|
||||
private static boolean allArgsNull(InsnNode insn) {
|
||||
for (InsnArg insnArg : insn.getArguments()) {
|
||||
if (insnArg.isLiteral()) {
|
||||
LiteralArg lit = (LiteralArg) insnArg;
|
||||
if (lit.getLiteral() != 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (!insnArg.isThis()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove instructions on 'move' chain until instruction with type 'insnType'
|
||||
*/
|
||||
private static InsnNode removeAssignChain(InsnNode insn, InstructionRemover remover, InsnType insnType) {
|
||||
if (insn == null) {
|
||||
return null;
|
||||
}
|
||||
remover.add(insn);
|
||||
InsnType type = insn.getType();
|
||||
if (type == insnType) {
|
||||
return insn;
|
||||
}
|
||||
if (type == InsnType.MOVE) {
|
||||
RegisterArg arg = (RegisterArg) insn.getArg(0);
|
||||
return removeAssignChain(arg.getAssignInsn(), remover, insnType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary instructions
|
||||
*/
|
||||
private static void removeStep(MethodNode mth) {
|
||||
private static void removeStep(MethodNode mth, InstructionRemover remover) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
InstructionRemover remover = new InstructionRemover(mth, block);
|
||||
|
||||
remover.setBlock(block);
|
||||
int size = block.getInstructions().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
InsnNode insn = block.getInstructions().get(i);
|
||||
|
||||
switch (insn.getType()) {
|
||||
case NOP:
|
||||
case GOTO:
|
||||
@@ -201,6 +291,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
ExceptionHandler excHandler = handlerAttr.getHandler();
|
||||
boolean noExitNode = true; // check if handler has exit edge to block not from this handler
|
||||
boolean reThrow = false;
|
||||
for (BlockNode excBlock : excHandler.getBlocks()) {
|
||||
if (noExitNode) {
|
||||
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
|
||||
@@ -211,11 +302,11 @@ public class ModVisitor extends AbstractVisitor {
|
||||
if (excHandler.isCatchAll()
|
||||
&& size > 0
|
||||
&& insns.get(size - 1).getType() == InsnType.THROW) {
|
||||
|
||||
reThrow = true;
|
||||
InstructionRemover.remove(mth, excBlock, size - 1);
|
||||
|
||||
// move not removed instructions to 'finally' block
|
||||
if (insns.size() != 0) {
|
||||
if (!insns.isEmpty()) {
|
||||
// TODO: support instructions from several blocks
|
||||
// tryBlock.setFinalBlockFromInsns(mth, insns);
|
||||
// TODO: because of incomplete realization don't extract final block,
|
||||
@@ -226,19 +317,36 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
|
||||
List<InsnNode> blockInsns = block.getInstructions();
|
||||
if (blockInsns.size() > 0) {
|
||||
if (!blockInsns.isEmpty()) {
|
||||
InsnNode insn = blockInsns.get(0);
|
||||
if (insn.getType() == InsnType.MOVE_EXCEPTION
|
||||
&& insn.getResult().getSVar().getUseCount() == 0) {
|
||||
InstructionRemover.remove(mth, block, 0);
|
||||
if (insn.getType() == InsnType.MOVE_EXCEPTION) {
|
||||
// result arg used both in this insn and exception handler,
|
||||
RegisterArg resArg = insn.getResult();
|
||||
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
|
||||
String name = excHandler.isCatchAll() ? "th" : "e";
|
||||
if (resArg.getName() == null) {
|
||||
resArg.setName(name);
|
||||
}
|
||||
SSAVar sVar = insn.getResult().getSVar();
|
||||
if (sVar.getUseCount() == 0) {
|
||||
excHandler.setArg(new NamedArg(name, type));
|
||||
InstructionRemover.remove(mth, block, 0);
|
||||
} else if (sVar.isUsedInPhi()) {
|
||||
// exception var moved to external variable => replace with 'move' insn
|
||||
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
|
||||
moveInsn.setResult(insn.getResult());
|
||||
NamedArg namedArg = new NamedArg(name, type);
|
||||
moveInsn.addArg(namedArg);
|
||||
excHandler.setArg(namedArg);
|
||||
replaceInsn(block, 0, moveInsn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalSize = 0;
|
||||
for (BlockNode excBlock : excHandler.getBlocks()) {
|
||||
totalSize += excBlock.getInstructions().size();
|
||||
}
|
||||
if (totalSize == 0 && noExitNode) {
|
||||
if (totalSize == 0 && noExitNode && reThrow) {
|
||||
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
|
||||
}
|
||||
}
|
||||
@@ -253,16 +361,6 @@ public class ModVisitor extends AbstractVisitor {
|
||||
block.getInstructions().set(i, insn);
|
||||
}
|
||||
|
||||
/**
|
||||
* In argument not used in other instructions then remove assign instruction.
|
||||
*/
|
||||
private static void removeInsnForArg(InstructionRemover remover, RegisterArg arg) {
|
||||
if (arg.getSVar().getUseCount() == 0
|
||||
&& arg.getAssignInsn() != null) {
|
||||
remover.add(arg.getAssignInsn());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkArgsNames(MethodNode mth) {
|
||||
for (RegisterArg arg : mth.getArguments(false)) {
|
||||
String name = arg.getName();
|
||||
|
||||
@@ -32,7 +32,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
||||
for (BlockNode block : blocks) {
|
||||
removeInstructions(block);
|
||||
checkInline(block);
|
||||
removeParenthesis(block);
|
||||
// removeParenthesis(block);
|
||||
modifyArith(block);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
||||
case NOP:
|
||||
case MONITOR_ENTER:
|
||||
case MONITOR_EXIT:
|
||||
case MOVE_EXCEPTION:
|
||||
it.remove();
|
||||
break;
|
||||
|
||||
@@ -123,25 +124,20 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
||||
*/
|
||||
private static void modifyArith(BlockNode block) {
|
||||
List<InsnNode> list = block.getInstructions();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
InsnNode insn = list.get(i);
|
||||
if (insn.getType() != InsnType.ARITH) {
|
||||
continue;
|
||||
}
|
||||
ArithNode arith = (ArithNode) insn;
|
||||
RegisterArg res = arith.getResult();
|
||||
InsnArg arg = arith.getArg(0);
|
||||
boolean replace = false;
|
||||
|
||||
if (res.equals(arg)) {
|
||||
replace = true;
|
||||
} else if (arg.isRegister()) {
|
||||
RegisterArg regArg = (RegisterArg) arg;
|
||||
replace = res.equalRegisterAndType(regArg);
|
||||
}
|
||||
if (replace) {
|
||||
ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1));
|
||||
list.set(i, newArith);
|
||||
for (InsnNode insn : list) {
|
||||
if (insn.getType() == InsnType.ARITH) {
|
||||
RegisterArg res = insn.getResult();
|
||||
InsnArg arg = insn.getArg(0);
|
||||
boolean replace = false;
|
||||
if (res.equals(arg)) {
|
||||
replace = true;
|
||||
} else if (arg.isRegister()) {
|
||||
RegisterArg regArg = (RegisterArg) arg;
|
||||
replace = res.equalRegisterAndType(regArg);
|
||||
}
|
||||
if (replace) {
|
||||
insn.add(AFlag.ARITH_ONEARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.util.List;
|
||||
@@ -43,6 +55,9 @@ public class ReSugarCode extends AbstractVisitor {
|
||||
case NEW_ARRAY:
|
||||
return processNewArray(mth, instructions, i, remover);
|
||||
|
||||
case SWITCH:
|
||||
return processEnumSwitch(mth, (SwitchNode) insn);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -75,4 +90,170 @@ public class ReSugarCode extends AbstractVisitor {
|
||||
}
|
||||
return filledArr;
|
||||
}
|
||||
|
||||
private static InsnNode processEnumSwitch(MethodNode mth, SwitchNode insn) {
|
||||
InsnArg arg = insn.getArg(0);
|
||||
if (!arg.isInsnWrap()) {
|
||||
return null;
|
||||
}
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (wrapInsn.getType() != InsnType.AGET) {
|
||||
return null;
|
||||
}
|
||||
EnumMapInfo enumMapInfo = checkEnumMapAccess(mth, wrapInsn);
|
||||
if (enumMapInfo == null) {
|
||||
return null;
|
||||
}
|
||||
FieldNode enumMapField = enumMapInfo.getMapField();
|
||||
InsnArg invArg = enumMapInfo.getArg();
|
||||
|
||||
EnumMapAttr.KeyValueMap valueMap = getEnumMap(mth, enumMapField);
|
||||
if (valueMap == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] keys = insn.getKeys();
|
||||
for (Object key : keys) {
|
||||
Object newKey = valueMap.get(key);
|
||||
if (newKey == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// replace confirmed
|
||||
if (!insn.replaceArg(arg, invArg)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
keys[i] = valueMap.get(keys[i]);
|
||||
}
|
||||
enumMapField.add(AFlag.DONT_GENERATE);
|
||||
checkAndHideClass(enumMapField.getParentClass());
|
||||
return null;
|
||||
}
|
||||
|
||||
private static EnumMapAttr.KeyValueMap getEnumMap(MethodNode mth, FieldNode field) {
|
||||
ClassNode syntheticClass = field.getParentClass();
|
||||
EnumMapAttr mapAttr = syntheticClass.get(AType.ENUM_MAP);
|
||||
if (mapAttr != null) {
|
||||
return mapAttr.getMap(field);
|
||||
}
|
||||
mapAttr = new EnumMapAttr();
|
||||
syntheticClass.addAttr(mapAttr);
|
||||
|
||||
MethodNode clsInitMth = syntheticClass.searchMethodByName("<clinit>()V");
|
||||
if (clsInitMth == null || clsInitMth.isNoCode()) {
|
||||
return null;
|
||||
}
|
||||
if (clsInitMth.getBasicBlocks() == null) {
|
||||
try {
|
||||
clsInitMth.load();
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Load failed", e);
|
||||
return null;
|
||||
}
|
||||
if (clsInitMth.getBasicBlocks() == null) {
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (BlockNode block : clsInitMth.getBasicBlocks()) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (insn.getType() == InsnType.APUT) {
|
||||
addToEnumMap(mth, mapAttr, insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapAttr.getMap(field);
|
||||
}
|
||||
|
||||
private static void addToEnumMap(MethodNode mth, EnumMapAttr mapAttr, InsnNode aputInsn) {
|
||||
InsnArg litArg = aputInsn.getArg(2);
|
||||
if (!litArg.isLiteral()) {
|
||||
return;
|
||||
}
|
||||
EnumMapInfo mapInfo = checkEnumMapAccess(mth, aputInsn);
|
||||
if (mapInfo == null) {
|
||||
return;
|
||||
}
|
||||
InsnArg enumArg = mapInfo.getArg();
|
||||
FieldNode field = mapInfo.getMapField();
|
||||
if (field == null || !enumArg.isInsnWrap()) {
|
||||
return;
|
||||
}
|
||||
InsnNode sget = ((InsnWrapArg) enumArg).getWrapInsn();
|
||||
if (!(sget instanceof IndexInsnNode)) {
|
||||
return;
|
||||
}
|
||||
Object index = ((IndexInsnNode) sget).getIndex();
|
||||
if (!(index instanceof FieldInfo)) {
|
||||
return;
|
||||
}
|
||||
FieldNode fieldNode = mth.dex().resolveField((FieldInfo) index);
|
||||
if (fieldNode == null) {
|
||||
return;
|
||||
}
|
||||
int literal = (int) ((LiteralArg) litArg).getLiteral();
|
||||
mapAttr.add(field, literal, fieldNode);
|
||||
}
|
||||
|
||||
public static EnumMapInfo checkEnumMapAccess(MethodNode mth, InsnNode checkInsn) {
|
||||
InsnArg sgetArg = checkInsn.getArg(0);
|
||||
InsnArg invArg = checkInsn.getArg(1);
|
||||
if (!sgetArg.isInsnWrap() || !invArg.isInsnWrap()) {
|
||||
return null;
|
||||
}
|
||||
InsnNode invInsn = ((InsnWrapArg) invArg).getWrapInsn();
|
||||
InsnNode sgetInsn = ((InsnWrapArg) sgetArg).getWrapInsn();
|
||||
if (invInsn.getType() != InsnType.INVOKE || sgetInsn.getType() != InsnType.SGET) {
|
||||
return null;
|
||||
}
|
||||
InvokeNode inv = (InvokeNode) invInsn;
|
||||
if (!inv.getCallMth().getShortId().equals("ordinal()I")) {
|
||||
return null;
|
||||
}
|
||||
ClassNode enumCls = mth.dex().resolveClass(inv.getCallMth().getDeclClass());
|
||||
if (enumCls == null || !enumCls.isEnum()) {
|
||||
return null;
|
||||
}
|
||||
Object index = ((IndexInsnNode) sgetInsn).getIndex();
|
||||
if (!(index instanceof FieldInfo)) {
|
||||
return null;
|
||||
}
|
||||
FieldNode enumMapField = mth.dex().resolveField((FieldInfo) index);
|
||||
if (enumMapField == null || !enumMapField.getAccessFlags().isSynthetic()) {
|
||||
return null;
|
||||
}
|
||||
return new EnumMapInfo(inv.getArg(0), enumMapField);
|
||||
}
|
||||
|
||||
/**
|
||||
* If all static final synthetic fields have DONT_GENERATE => hide whole class
|
||||
*/
|
||||
private static void checkAndHideClass(ClassNode cls) {
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
AccessInfo af = field.getAccessFlags();
|
||||
if (af.isSynthetic() && af.isStatic() && af.isFinal()
|
||||
&& !field.contains(AFlag.DONT_GENERATE)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
|
||||
private static class EnumMapInfo {
|
||||
private final InsnArg arg;
|
||||
private final FieldNode mapField;
|
||||
|
||||
public EnumMapInfo(InsnArg arg, FieldNode mapField) {
|
||||
this.arg = arg;
|
||||
this.mapField = mapField;
|
||||
}
|
||||
|
||||
public InsnArg getArg() {
|
||||
return arg;
|
||||
}
|
||||
|
||||
public FieldNode getMapField() {
|
||||
return mapField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -62,6 +64,9 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
case IF:
|
||||
simplifyIf((IfNode) insn);
|
||||
break;
|
||||
case TERNARY:
|
||||
simplifyTernary((TernaryInsn) insn);
|
||||
break;
|
||||
|
||||
case INVOKE:
|
||||
return convertInvoke(mth, insn);
|
||||
@@ -99,12 +104,24 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
&& ((LiteralArg) insn.getArg(1)).getLiteral() == 0) {
|
||||
insn.changeCondition(insn.getOp(), wi.getArg(0), wi.getArg(1));
|
||||
} else {
|
||||
LOG.warn("TODO: cmp" + insn);
|
||||
LOG.warn("TODO: cmp {}", insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify condition in ternary operation
|
||||
*/
|
||||
private static void simplifyTernary(TernaryInsn insn) {
|
||||
IfCondition condition = insn.getCondition();
|
||||
if (condition.isCompare()) {
|
||||
simplifyIf(condition.getCompare().getInsn());
|
||||
} else {
|
||||
insn.simplifyCondition();
|
||||
}
|
||||
}
|
||||
|
||||
private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) {
|
||||
MethodInfo callMth = ((InvokeNode) insn).getCallMth();
|
||||
if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER)
|
||||
|
||||
@@ -6,7 +6,7 @@ import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.LoopRegion;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
@@ -72,7 +72,6 @@ public class CheckRegions extends AbstractVisitor {
|
||||
BlockNode loopHeader = ((LoopRegion) region).getHeader();
|
||||
if (loopHeader != null && loopHeader.getInstructions().size() != 1) {
|
||||
ErrorsCounter.methodError(mth, "Incorrect condition in loop: " + loopHeader);
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package jadx.core.dex.visitors.regions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -9,9 +10,9 @@ import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.IfCondition.Mode;
|
||||
import jadx.core.dex.regions.IfInfo;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.dex.regions.conditions.IfCondition.Mode;
|
||||
import jadx.core.dex.regions.conditions.IfInfo;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -21,6 +22,7 @@ import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.utils.BlockUtils.getNextBlock;
|
||||
import static jadx.core.utils.BlockUtils.isPathExists;
|
||||
|
||||
public class IfMakerHelper {
|
||||
@@ -38,6 +40,11 @@ public class IfMakerHelper {
|
||||
return info;
|
||||
}
|
||||
|
||||
static IfInfo searchNestedIf(IfInfo info) {
|
||||
IfInfo tmp = mergeNestedIfNodes(info);
|
||||
return tmp != null ? tmp : info;
|
||||
}
|
||||
|
||||
static IfInfo restructureIf(MethodNode mth, BlockNode block, IfInfo info) {
|
||||
final BlockNode thenBlock = info.getThenBlock();
|
||||
final BlockNode elseBlock = info.getElseBlock();
|
||||
@@ -47,18 +54,18 @@ public class IfMakerHelper {
|
||||
info.setOutBlock(null);
|
||||
return info;
|
||||
}
|
||||
boolean badThen = !allPathsFromIf(thenBlock, info);
|
||||
boolean badElse = !allPathsFromIf(elseBlock, info);
|
||||
boolean badThen = isBadBranchBlock(info, thenBlock);
|
||||
boolean badElse = isBadBranchBlock(info, elseBlock);
|
||||
if (badThen && badElse) {
|
||||
LOG.debug("Stop processing blocks after 'if': {}, method: {}", info, mth);
|
||||
LOG.debug("Stop processing blocks after 'if': {}, method: {}", info.getIfBlock(), mth);
|
||||
return null;
|
||||
}
|
||||
if (badElse) {
|
||||
info = new IfInfo(info.getCondition(), thenBlock, null);
|
||||
info = new IfInfo(info, thenBlock, null);
|
||||
info.setOutBlock(elseBlock);
|
||||
} else if (badThen) {
|
||||
info = IfInfo.invert(info);
|
||||
info = new IfInfo(info.getCondition(), elseBlock, null);
|
||||
info = new IfInfo(info, elseBlock, null);
|
||||
info.setOutBlock(thenBlock);
|
||||
} else {
|
||||
List<BlockNode> thenSC = thenBlock.getCleanSuccessors();
|
||||
@@ -86,6 +93,26 @@ public class IfMakerHelper {
|
||||
return info;
|
||||
}
|
||||
|
||||
private static boolean isBadBranchBlock(IfInfo info, BlockNode block) {
|
||||
// check if block at end of loop edge
|
||||
if (block.contains(AFlag.LOOP_START) && block.getPredecessors().size() == 1) {
|
||||
BlockNode pred = block.getPredecessors().get(0);
|
||||
if (pred.contains(AFlag.LOOP_END)) {
|
||||
List<LoopInfo> startLoops = block.getAll(AType.LOOP);
|
||||
List<LoopInfo> endLoops = pred.getAll(AType.LOOP);
|
||||
// search for same loop
|
||||
for (LoopInfo startLoop : startLoops) {
|
||||
for (LoopInfo endLoop : endLoops) {
|
||||
if (startLoop == endLoop) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !allPathsFromIf(block, info);
|
||||
}
|
||||
|
||||
private static boolean allPathsFromIf(BlockNode block, IfInfo info) {
|
||||
List<BlockNode> preds = block.getPredecessors();
|
||||
Set<BlockNode> ifBlocks = info.getMergedBlocks();
|
||||
@@ -126,12 +153,13 @@ public class IfMakerHelper {
|
||||
if (!RegionMaker.isEqualPaths(curElse, nextIf.getElseBlock())
|
||||
&& !RegionMaker.isEqualPaths(curThen, nextIf.getThenBlock())) {
|
||||
// complex condition, run additional checks
|
||||
if (checkConditionBranches(curThen, curElse) || checkConditionBranches(curElse, curThen)) {
|
||||
if (checkConditionBranches(curThen, curElse)
|
||||
|| checkConditionBranches(curElse, curThen)) {
|
||||
return null;
|
||||
}
|
||||
BlockNode otherBranchBlock = followThenBranch ? curElse : curThen;
|
||||
if (!isPathExists(nextIf.getIfBlock(), otherBranchBlock)) {
|
||||
return null;
|
||||
return checkForTernaryInCondition(currentIf);
|
||||
}
|
||||
if (isPathExists(nextIf.getThenBlock(), otherBranchBlock)
|
||||
&& isPathExists(nextIf.getElseBlock(), otherBranchBlock)) {
|
||||
@@ -147,15 +175,46 @@ public class IfMakerHelper {
|
||||
if (isInversionNeeded(currentIf, nextIf)) {
|
||||
nextIf = IfInfo.invert(nextIf);
|
||||
}
|
||||
} else {
|
||||
return currentIf;
|
||||
}
|
||||
}
|
||||
|
||||
IfInfo result = mergeIfInfo(currentIf, nextIf, followThenBranch);
|
||||
// search next nested if block
|
||||
IfInfo next = mergeNestedIfNodes(result);
|
||||
if (next != null) {
|
||||
return next;
|
||||
return searchNestedIf(result);
|
||||
}
|
||||
|
||||
private static IfInfo checkForTernaryInCondition(IfInfo currentIf) {
|
||||
IfInfo nextThen = getNextIf(currentIf, currentIf.getThenBlock());
|
||||
IfInfo nextElse = getNextIf(currentIf, currentIf.getElseBlock());
|
||||
if (nextThen == null || nextElse == null) {
|
||||
return null;
|
||||
}
|
||||
if (!nextThen.getIfBlock().getDomFrontier().equals(nextElse.getIfBlock().getDomFrontier())) {
|
||||
return null;
|
||||
}
|
||||
nextThen = searchNestedIf(nextThen);
|
||||
nextElse = searchNestedIf(nextElse);
|
||||
if (nextThen.getThenBlock() == nextElse.getThenBlock()
|
||||
&& nextThen.getElseBlock() == nextElse.getElseBlock()) {
|
||||
return mergeTernaryConditions(currentIf, nextThen, nextElse);
|
||||
}
|
||||
if (nextThen.getThenBlock() == nextElse.getElseBlock()
|
||||
&& nextThen.getElseBlock() == nextElse.getThenBlock()) {
|
||||
nextElse = IfInfo.invert(nextElse);
|
||||
return mergeTernaryConditions(currentIf, nextThen, nextElse);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IfInfo mergeTernaryConditions(IfInfo currentIf, IfInfo nextThen, IfInfo nextElse) {
|
||||
IfCondition newCondition = IfCondition.ternary(currentIf.getCondition(),
|
||||
nextThen.getCondition(), nextElse.getCondition());
|
||||
IfInfo result = new IfInfo(newCondition, nextThen.getThenBlock(), nextThen.getElseBlock());
|
||||
result.setIfBlock(currentIf.getIfBlock());
|
||||
result.merge(currentIf, nextThen, nextElse);
|
||||
confirmMerge(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -170,19 +229,33 @@ public class IfMakerHelper {
|
||||
|
||||
private static IfInfo mergeIfInfo(IfInfo first, IfInfo second, boolean followThenBranch) {
|
||||
Mode mergeOperation = followThenBranch ? Mode.AND : Mode.OR;
|
||||
BlockNode otherPathBlock = followThenBranch ? first.getElseBlock() : first.getThenBlock();
|
||||
RegionMaker.skipSimplePath(otherPathBlock);
|
||||
first.getIfBlock().add(AFlag.SKIP);
|
||||
second.getIfBlock().add(AFlag.SKIP);
|
||||
|
||||
IfCondition condition = IfCondition.merge(mergeOperation, first.getCondition(), second.getCondition());
|
||||
IfInfo result = new IfInfo(condition, second);
|
||||
result.setIfBlock(first.getIfBlock());
|
||||
result.getMergedBlocks().addAll(first.getMergedBlocks());
|
||||
result.getMergedBlocks().addAll(second.getMergedBlocks());
|
||||
result.merge(first, second);
|
||||
|
||||
BlockNode otherPathBlock = followThenBranch ? first.getElseBlock() : first.getThenBlock();
|
||||
skipSimplePath(otherPathBlock, result.getSkipBlocks());
|
||||
return result;
|
||||
}
|
||||
|
||||
static void confirmMerge(IfInfo info) {
|
||||
if (info.getMergedBlocks().size() > 1) {
|
||||
for (BlockNode block : info.getMergedBlocks()) {
|
||||
if (block != info.getIfBlock()) {
|
||||
block.add(AFlag.SKIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!info.getSkipBlocks().isEmpty()) {
|
||||
for (BlockNode block : info.getSkipBlocks()) {
|
||||
block.add(AFlag.SKIP);
|
||||
}
|
||||
info.getSkipBlocks().clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static IfInfo getNextIf(IfInfo info, BlockNode block) {
|
||||
if (!canSelectNext(info, block)) {
|
||||
return null;
|
||||
@@ -250,4 +323,13 @@ public class IfMakerHelper {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void skipSimplePath(BlockNode block, Set<BlockNode> skipped) {
|
||||
while (block != null
|
||||
&& block.getCleanSuccessors().size() < 2
|
||||
&& block.getPredecessors().size() == 1) {
|
||||
skipped.add(block);
|
||||
block = getNextBlock(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.IfCondition.Mode;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.dex.regions.conditions.IfCondition.Mode;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
|
||||
@@ -21,6 +21,16 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
// collapse ternary operators
|
||||
DepthRegionTraversal.traverseAllIterative(mth, new IRegionIterativeVisitor() {
|
||||
@Override
|
||||
public boolean visitRegion(MethodNode mth, IRegion region) {
|
||||
if (region instanceof IfRegion) {
|
||||
return TernaryMod.makeTernaryInsn(mth, (IfRegion) region);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
DepthRegionTraversal.traverseAll(mth, this);
|
||||
DepthRegionTraversal.traverseAllIterative(mth, this);
|
||||
}
|
||||
@@ -35,7 +45,7 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
||||
@Override
|
||||
public boolean visitRegion(MethodNode mth, IRegion region) {
|
||||
if (region instanceof IfRegion) {
|
||||
return removeRedundantElseBlock((IfRegion) region);
|
||||
return removeRedundantElseBlock(mth, (IfRegion) region);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -53,8 +63,6 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
||||
moveReturnToThenBlock(mth, ifRegion);
|
||||
moveBreakToThenBlock(ifRegion);
|
||||
markElseIfChains(ifRegion);
|
||||
|
||||
TernaryMod.makeTernaryInsn(mth, ifRegion);
|
||||
}
|
||||
|
||||
private static void simplifyIfCondition(IfRegion ifRegion) {
|
||||
@@ -128,28 +136,38 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean removeRedundantElseBlock(IfRegion ifRegion) {
|
||||
if (ifRegion.getElseRegion() != null
|
||||
&& !ifRegion.contains(AFlag.ELSE_IF_CHAIN)
|
||||
&& !ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN)
|
||||
&& hasBranchTerminator(ifRegion)
|
||||
&& insnsCount(ifRegion.getThenRegion()) < 2) {
|
||||
IRegion parent = ifRegion.getParent();
|
||||
Region newRegion = new Region(parent);
|
||||
if (parent.replaceSubBlock(ifRegion, newRegion)) {
|
||||
newRegion.add(ifRegion);
|
||||
newRegion.add(ifRegion.getElseRegion());
|
||||
ifRegion.setElseRegion(null);
|
||||
return true;
|
||||
}
|
||||
private static boolean removeRedundantElseBlock(MethodNode mth, IfRegion ifRegion) {
|
||||
if (ifRegion.getElseRegion() == null
|
||||
|| ifRegion.contains(AFlag.ELSE_IF_CHAIN)
|
||||
|| ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN)) {
|
||||
return false;
|
||||
}
|
||||
if (!hasBranchTerminator(ifRegion.getThenRegion())) {
|
||||
return false;
|
||||
}
|
||||
// code style check:
|
||||
// will remove 'return;' from 'then' and 'else' with one instruction
|
||||
// see #jadx.tests.integration.conditions.TestConditions9
|
||||
if (mth.getReturnType() == ArgType.VOID
|
||||
&& insnsCount(ifRegion.getThenRegion()) == 2
|
||||
&& insnsCount(ifRegion.getElseRegion()) == 2) {
|
||||
return false;
|
||||
}
|
||||
IRegion parent = ifRegion.getParent();
|
||||
Region newRegion = new Region(parent);
|
||||
if (parent.replaceSubBlock(ifRegion, newRegion)) {
|
||||
newRegion.add(ifRegion);
|
||||
newRegion.add(ifRegion.getElseRegion());
|
||||
ifRegion.setElseRegion(null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasBranchTerminator(IfRegion ifRegion) {
|
||||
private static boolean hasBranchTerminator(IContainer region) {
|
||||
// TODO: check for exception throw
|
||||
return RegionUtils.hasExitBlock(ifRegion.getThenRegion())
|
||||
|| RegionUtils.hasBreakInsn(ifRegion.getThenRegion());
|
||||
return RegionUtils.hasExitBlock(region)
|
||||
|| RegionUtils.hasBreakInsn(region);
|
||||
}
|
||||
|
||||
private static void invertIfRegion(IfRegion ifRegion) {
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
package jadx.core.dex.visitors.regions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.ArithOp;
|
||||
import jadx.core.dex.instructions.IfOp;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.InvokeType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IBlock;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.conditions.Compare;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.dex.regions.loops.ForEachLoop;
|
||||
import jadx.core.dex.regions.loops.ForLoop;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.regions.loops.LoopType;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoopRegionVisitor.class);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) {
|
||||
DepthRegionTraversal.traverseAll(mth, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterRegion(MethodNode mth, IRegion region) {
|
||||
if (region instanceof LoopRegion) {
|
||||
processLoopRegion(mth, (LoopRegion) region);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processLoopRegion(MethodNode mth, LoopRegion loopRegion) {
|
||||
if (loopRegion.isConditionAtEnd()) {
|
||||
return;
|
||||
}
|
||||
IfCondition condition = loopRegion.getCondition();
|
||||
if (condition == null) {
|
||||
return;
|
||||
}
|
||||
if (checkForIndexedLoop(mth, loopRegion, condition)) {
|
||||
return;
|
||||
}
|
||||
if (checkIterableForEach(mth, loopRegion, condition)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for indexed loop.
|
||||
*/
|
||||
private static boolean checkForIndexedLoop(MethodNode mth, LoopRegion loopRegion, IfCondition condition) {
|
||||
InsnNode incrInsn = RegionUtils.getLastInsn(loopRegion);
|
||||
if (incrInsn == null) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg incrArg = incrInsn.getResult();
|
||||
if (incrArg == null
|
||||
|| incrArg.getSVar() == null
|
||||
|| !incrArg.getSVar().isUsedInPhi()) {
|
||||
return false;
|
||||
}
|
||||
PhiInsn phiInsn = incrArg.getSVar().getUsedInPhi();
|
||||
if (phiInsn == null
|
||||
|| phiInsn.getArgsCount() != 2
|
||||
|| !phiInsn.getArg(1).equals(incrArg)
|
||||
|| incrArg.getSVar().getUseCount() != 1) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg arg = phiInsn.getResult();
|
||||
List<RegisterArg> condArgs = condition.getRegisterArgs();
|
||||
if (!condArgs.contains(arg) || arg.getSVar().isUsedInPhi()) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg initArg = phiInsn.getArg(0);
|
||||
InsnNode initInsn = initArg.getAssignInsn();
|
||||
if (initInsn == null || initArg.getSVar().getUseCount() != 1) {
|
||||
return false;
|
||||
}
|
||||
if (!usedOnlyInLoop(mth, loopRegion, arg)) {
|
||||
return false;
|
||||
}
|
||||
// can't make loop if argument from increment instruction is assign in loop
|
||||
List<RegisterArg> args = new LinkedList<RegisterArg>();
|
||||
incrInsn.getRegisterArgs(args);
|
||||
for (RegisterArg iArg : args) {
|
||||
if (assignOnlyInLoop(mth, loopRegion, iArg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// all checks passed
|
||||
initInsn.add(AFlag.SKIP);
|
||||
incrInsn.add(AFlag.SKIP);
|
||||
LoopType arrForEach = checkArrayForEach(mth, initInsn, incrInsn, condition);
|
||||
if (arrForEach != null) {
|
||||
loopRegion.setType(arrForEach);
|
||||
return true;
|
||||
}
|
||||
loopRegion.setType(new ForLoop(initInsn, incrInsn));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static LoopType checkArrayForEach(MethodNode mth, InsnNode initInsn, InsnNode incrInsn, IfCondition condition) {
|
||||
if (!(incrInsn instanceof ArithNode)) {
|
||||
return null;
|
||||
}
|
||||
ArithNode arithNode = (ArithNode) incrInsn;
|
||||
if (arithNode.getOp() != ArithOp.ADD) {
|
||||
return null;
|
||||
}
|
||||
InsnArg lit = incrInsn.getArg(1);
|
||||
if (!lit.isLiteral() || ((LiteralArg) lit).getLiteral() != 1) {
|
||||
return null;
|
||||
}
|
||||
if (initInsn.getType() != InsnType.CONST
|
||||
|| !initInsn.getArg(0).isLiteral()
|
||||
|| ((LiteralArg) initInsn.getArg(0)).getLiteral() != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InsnArg condArg = incrInsn.getArg(0);
|
||||
if (!condArg.isRegister()) {
|
||||
return null;
|
||||
}
|
||||
SSAVar sVar = ((RegisterArg) condArg).getSVar();
|
||||
List<RegisterArg> args = sVar.getUseList();
|
||||
if (args.size() != 3 || args.get(2) != condArg) {
|
||||
return null;
|
||||
}
|
||||
condArg = args.get(0);
|
||||
RegisterArg arrIndex = args.get(1);
|
||||
InsnNode arrGetInsn = arrIndex.getParentInsn();
|
||||
if (arrGetInsn == null || arrGetInsn.getType() != InsnType.AGET) {
|
||||
return null;
|
||||
}
|
||||
if (!condition.isCompare()) {
|
||||
return null;
|
||||
}
|
||||
Compare compare = condition.getCompare();
|
||||
if (compare.getOp() != IfOp.LT || compare.getA() != condArg) {
|
||||
return null;
|
||||
}
|
||||
InsnNode len;
|
||||
InsnArg bCondArg = compare.getB();
|
||||
if (bCondArg.isInsnWrap()) {
|
||||
len = ((InsnWrapArg) bCondArg).getWrapInsn();
|
||||
} else if (bCondArg.isRegister()) {
|
||||
len = ((RegisterArg) bCondArg).getAssignInsn();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (len == null || len.getType() != InsnType.ARRAY_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
InsnArg arrayArg = len.getArg(0);
|
||||
if (!arrayArg.equals(arrGetInsn.getArg(0))) {
|
||||
return null;
|
||||
}
|
||||
RegisterArg iterVar = arrGetInsn.getResult();
|
||||
if (iterVar == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// array for each loop confirmed
|
||||
len.add(AFlag.SKIP);
|
||||
arrGetInsn.add(AFlag.SKIP);
|
||||
InstructionRemover.unbindInsn(mth, len);
|
||||
|
||||
// inline array variable
|
||||
CodeShrinker.shrinkMethod(mth);
|
||||
if (arrGetInsn.contains(AFlag.WRAPPED)) {
|
||||
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn);
|
||||
if (wrapArg != null) {
|
||||
wrapArg.getParentInsn().replaceArg(wrapArg, iterVar);
|
||||
} else {
|
||||
LOG.debug(" checkArrayForEach: Wrapped insn not found: {}, mth: {}", arrGetInsn, mth);
|
||||
}
|
||||
}
|
||||
return new ForEachLoop(iterVar, len.getArg(0));
|
||||
}
|
||||
|
||||
private static boolean checkIterableForEach(MethodNode mth, LoopRegion loopRegion, IfCondition condition) {
|
||||
List<RegisterArg> condArgs = condition.getRegisterArgs();
|
||||
if (condArgs.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg iteratorArg = condArgs.get(0);
|
||||
SSAVar sVar = iteratorArg.getSVar();
|
||||
if (sVar == null || sVar.isUsedInPhi()) {
|
||||
return false;
|
||||
}
|
||||
List<RegisterArg> useList = sVar.getUseList();
|
||||
InsnNode assignInsn = iteratorArg.getAssignInsn();
|
||||
if (useList.size() != 2
|
||||
|| assignInsn == null
|
||||
|| !checkInvoke(assignInsn, null, "iterator()Ljava/util/Iterator;", 0)) {
|
||||
return false;
|
||||
}
|
||||
InsnArg iterableArg = assignInsn.getArg(0);
|
||||
InsnNode hasNextCall = useList.get(0).getParentInsn();
|
||||
InsnNode nextCall = useList.get(1).getParentInsn();
|
||||
if (!checkInvoke(hasNextCall, "java.util.Iterator", "hasNext()Z", 0)
|
||||
|| !checkInvoke(nextCall, "java.util.Iterator", "next()Ljava/lang/Object;", 0)) {
|
||||
return false;
|
||||
}
|
||||
List<InsnNode> toSkip = new LinkedList<InsnNode>();
|
||||
RegisterArg iterVar = nextCall.getResult();
|
||||
if (nextCall.contains(AFlag.WRAPPED)) {
|
||||
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, nextCall);
|
||||
if (wrapArg != null) {
|
||||
InsnNode parentInsn = wrapArg.getParentInsn();
|
||||
if (parentInsn.getType() != InsnType.CHECK_CAST) {
|
||||
parentInsn.replaceArg(wrapArg, iterVar);
|
||||
} else {
|
||||
iterVar = parentInsn.getResult();
|
||||
InsnArg castArg = BlockUtils.searchWrappedInsnParent(mth, parentInsn);
|
||||
if (castArg != null) {
|
||||
castArg.getParentInsn().replaceArg(castArg, iterVar);
|
||||
} else {
|
||||
// cast not inlined
|
||||
toSkip.add(parentInsn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn(" checkIterableForEach: Wrapped insn not found: {}, mth: {}", nextCall, mth);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
toSkip.add(nextCall);
|
||||
}
|
||||
if (iterVar == null || !fixIterableType(iterableArg, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assignInsn.add(AFlag.SKIP);
|
||||
for (InsnNode insnNode : toSkip) {
|
||||
insnNode.add(AFlag.SKIP);
|
||||
}
|
||||
loopRegion.setType(new ForEachLoop(iterVar, iterableArg));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean fixIterableType(InsnArg iterableArg, RegisterArg iterVar) {
|
||||
ArgType type = iterableArg.getType();
|
||||
if (type.isGeneric()) {
|
||||
ArgType[] genericTypes = type.getGenericTypes();
|
||||
if (genericTypes != null && genericTypes.length == 1) {
|
||||
ArgType gType = genericTypes[0];
|
||||
if (ArgType.isInstanceOf(gType, iterVar.getType())) {
|
||||
return true;
|
||||
} else {
|
||||
LOG.warn("Generic type differs: {} and {}", type, iterVar.getType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!iterableArg.isRegister()) {
|
||||
return true;
|
||||
}
|
||||
// TODO: add checks
|
||||
type = ArgType.generic(type.getObject(), new ArgType[]{iterVar.getType()});
|
||||
iterableArg.setType(type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if instruction is a interface invoke with corresponding parameters.
|
||||
*/
|
||||
private static boolean checkInvoke(InsnNode insn, String declClsFullName, String mthId, int argsCount) {
|
||||
if (insn.getType() == InsnType.INVOKE) {
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
MethodInfo callMth = inv.getCallMth();
|
||||
if (callMth.getArgsCount() == argsCount
|
||||
&& callMth.getShortId().equals(mthId)
|
||||
&& inv.getInvokeType() == InvokeType.INTERFACE) {
|
||||
return declClsFullName == null || callMth.getDeclClass().getFullName().equals(declClsFullName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean assignOnlyInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {
|
||||
InsnNode assignInsn = arg.getAssignInsn();
|
||||
if (assignInsn == null) {
|
||||
return true;
|
||||
}
|
||||
if (!argInLoop(mth, loopRegion, assignInsn.getResult())) {
|
||||
return false;
|
||||
}
|
||||
if (assignInsn instanceof PhiInsn) {
|
||||
PhiInsn phiInsn = (PhiInsn) assignInsn;
|
||||
for (InsnArg phiArg : phiInsn.getArguments()) {
|
||||
if (!assignOnlyInLoop(mth, loopRegion, (RegisterArg) phiArg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean usedOnlyInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {
|
||||
List<RegisterArg> useList = arg.getSVar().getUseList();
|
||||
for (RegisterArg useArg : useList) {
|
||||
if (!argInLoop(mth, loopRegion, useArg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean argInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) {
|
||||
InsnNode parentInsn = arg.getParentInsn();
|
||||
if (parentInsn == null) {
|
||||
return false;
|
||||
}
|
||||
BlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn);
|
||||
if (block == null) {
|
||||
LOG.debug(" LoopRegionVisitor: instruction not found: {}, mth: {}", parentInsn, mth);
|
||||
return false;
|
||||
}
|
||||
return RegionUtils.isRegionContainsBlock(loopRegion, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveRegion(MethodNode mth, IRegion region) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBlock(MethodNode mth, IBlock container) {
|
||||
}
|
||||
}
|
||||
+30
-16
@@ -1,6 +1,5 @@
|
||||
package jadx.core.dex.visitors.regions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
@@ -8,10 +7,13 @@ import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.AbstractRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.TryCatchRegion;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@@ -78,6 +80,10 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bs == null) {
|
||||
LOG.debug(" Can't build try/catch dominators bitset, tb: {}, mth: {} ", tb, mth);
|
||||
continue;
|
||||
}
|
||||
|
||||
// intersect to get dominator of dominators
|
||||
List<BlockNode> domBlocks = BlockUtils.bitSetToBlocks(mth, bs);
|
||||
@@ -94,7 +100,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
|
||||
TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
|
||||
if (prevTB != null) {
|
||||
LOG.info("!!! TODO: merge try blocks in " + mth);
|
||||
LOG.info("!!! TODO: merge try blocks in {}", mth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +112,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
if (region.getSubBlocks().contains(dominator)) {
|
||||
TryCatchBlock tb = tryBlocksMap.get(dominator);
|
||||
if (!wrapBlocks(region, tb, dominator)) {
|
||||
LOG.warn("Can't wrap try/catch for {}, method: {}", dominator, mth);
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
ErrorsCounter.methodError(mth, "Can't wrap try/catch for " + region);
|
||||
}
|
||||
tryBlocksMap.remove(dominator);
|
||||
return;
|
||||
@@ -118,34 +123,43 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
/**
|
||||
* Extract all block dominated by 'dominator' to separate region and mark as try/catch block
|
||||
*/
|
||||
private static boolean wrapBlocks(IRegion region, TryCatchBlock tb, BlockNode dominator) {
|
||||
Region newRegion = new Region(region);
|
||||
private static boolean wrapBlocks(IRegion replaceRegion, TryCatchBlock tb, BlockNode dominator) {
|
||||
IRegion region = replaceRegion;
|
||||
if (region instanceof LoopRegion) {
|
||||
LoopRegion loop = (LoopRegion) region;
|
||||
region = loop.getBody();
|
||||
}
|
||||
|
||||
Region tryRegion = new Region(region);
|
||||
List<IContainer> subBlocks = region.getSubBlocks();
|
||||
for (IContainer cont : subBlocks) {
|
||||
if (RegionUtils.isDominatedBy(dominator, cont)) {
|
||||
if (isHandlerPath(tb, cont)) {
|
||||
break;
|
||||
}
|
||||
newRegion.getSubBlocks().add(cont);
|
||||
tryRegion.getSubBlocks().add(cont);
|
||||
}
|
||||
}
|
||||
if (newRegion.getSubBlocks().isEmpty()) {
|
||||
if (tryRegion.getSubBlocks().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TryCatchRegion tryCatchRegion = new TryCatchRegion(region, tryRegion);
|
||||
tryRegion.setParent(tryCatchRegion);
|
||||
tryCatchRegion.setTryCatchBlock(tb.getCatchAttr().getTryBlock());
|
||||
|
||||
// replace first node by region
|
||||
IContainer firstNode = newRegion.getSubBlocks().get(0);
|
||||
if (!region.replaceSubBlock(firstNode, newRegion)) {
|
||||
IContainer firstNode = tryRegion.getSubBlocks().get(0);
|
||||
if (!region.replaceSubBlock(firstNode, tryCatchRegion)) {
|
||||
return false;
|
||||
}
|
||||
subBlocks.removeAll(newRegion.getSubBlocks());
|
||||
subBlocks.removeAll(tryRegion.getSubBlocks());
|
||||
|
||||
newRegion.addAttr(tb.getCatchAttr());
|
||||
|
||||
// fix parents
|
||||
for (IContainer cont : newRegion.getSubBlocks()) {
|
||||
// fix parents for tryRegion sub blocks
|
||||
for (IContainer cont : tryRegion.getSubBlocks()) {
|
||||
if (cont instanceof AbstractRegion) {
|
||||
AbstractRegion aReg = (AbstractRegion) cont;
|
||||
aReg.setParent(newRegion);
|
||||
aReg.setParent(tryRegion);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -4,6 +4,7 @@ import jadx.core.codegen.NameGen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.VarName;
|
||||
@@ -12,8 +13,11 @@ import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.SwitchRegion;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.regions.loops.ForLoop;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.regions.loops.LoopType;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
@@ -111,45 +115,77 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static class CollectUsageRegionVisitor extends TracedRegionVisitor {
|
||||
private final List<RegisterArg> args;
|
||||
private final Map<Variable, Usage> usageMap;
|
||||
|
||||
public CollectUsageRegionVisitor(Map<Variable, Usage> usageMap) {
|
||||
this.usageMap = usageMap;
|
||||
args = new ArrayList<RegisterArg>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBlockTraced(MethodNode mth, IBlock container, IRegion curRegion) {
|
||||
regionProcess(mth, curRegion);
|
||||
int len = container.getInstructions().size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InsnNode insn = container.getInstructions().get(i);
|
||||
if (insn.contains(AFlag.SKIP)) {
|
||||
continue;
|
||||
}
|
||||
args.clear();
|
||||
processInsn(insn, curRegion);
|
||||
}
|
||||
}
|
||||
|
||||
private void regionProcess(MethodNode mth, IRegion region) {
|
||||
if (region instanceof LoopRegion) {
|
||||
LoopRegion loopRegion = (LoopRegion) region;
|
||||
LoopType loopType = loopRegion.getType();
|
||||
if (loopType instanceof ForLoop) {
|
||||
ForLoop forLoop = (ForLoop) loopType;
|
||||
processInsn(forLoop.getInitInsn(), region);
|
||||
processInsn(forLoop.getIncrInsn(), region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processInsn(InsnNode insn, IRegion curRegion) {
|
||||
if (insn == null) {
|
||||
return;
|
||||
}
|
||||
// result
|
||||
RegisterArg result = insn.getResult();
|
||||
if (result != null && result.isRegister()) {
|
||||
Usage u = addToUsageMap(result, usageMap);
|
||||
if (u.getArg() == null) {
|
||||
u.setArg(result);
|
||||
u.setArgRegion(curRegion);
|
||||
}
|
||||
u.getAssigns().add(curRegion);
|
||||
}
|
||||
// args
|
||||
args.clear();
|
||||
insn.getRegisterArgs(args);
|
||||
for (RegisterArg arg : args) {
|
||||
Usage u = addToUsageMap(arg, usageMap);
|
||||
u.getUseRegions().add(curRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<Variable, Usage> usageMap = new LinkedHashMap<Variable, Usage>();
|
||||
for (RegisterArg arg : mth.getArguments(true)) {
|
||||
addToUsageMap(arg, usageMap);
|
||||
}
|
||||
|
||||
// collect all variables usage
|
||||
IRegionVisitor collect = new TracedRegionVisitor() {
|
||||
@Override
|
||||
public void processBlockTraced(MethodNode mth, IBlock container, IRegion curRegion) {
|
||||
int len = container.getInstructions().size();
|
||||
List<RegisterArg> args = new ArrayList<RegisterArg>();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InsnNode insn = container.getInstructions().get(i);
|
||||
// result
|
||||
RegisterArg result = insn.getResult();
|
||||
if (result != null && result.isRegister()) {
|
||||
Usage u = addToUsageMap(result, usageMap);
|
||||
if (u.getArg() == null) {
|
||||
u.setArg(result);
|
||||
u.setArgRegion(curRegion);
|
||||
}
|
||||
u.getAssigns().add(curRegion);
|
||||
}
|
||||
// args
|
||||
args.clear();
|
||||
insn.getRegisterArgs(args);
|
||||
for (RegisterArg arg : args) {
|
||||
Usage u = addToUsageMap(arg, usageMap);
|
||||
u.getUseRegions().add(curRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
IRegionVisitor collect = new CollectUsageRegionVisitor(usageMap);
|
||||
DepthRegionTraversal.traverseAll(mth, collect);
|
||||
|
||||
// reduce assigns map
|
||||
@@ -158,6 +194,22 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
usageMap.remove(new Variable(arg));
|
||||
}
|
||||
|
||||
Iterator<Entry<Variable, Usage>> umIt = usageMap.entrySet().iterator();
|
||||
while (umIt.hasNext()) {
|
||||
Entry<Variable, Usage> entry = umIt.next();
|
||||
Usage u = entry.getValue();
|
||||
// if no assigns => remove
|
||||
if (u.getAssigns().isEmpty()) {
|
||||
umIt.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// variable declared at 'catch' clause
|
||||
InsnNode parentInsn = u.getArg().getParentInsn();
|
||||
if (parentInsn == null || parentInsn.getType() == InsnType.MOVE_EXCEPTION) {
|
||||
umIt.remove();
|
||||
}
|
||||
}
|
||||
if (usageMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -168,24 +220,20 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
for (Iterator<Entry<Variable, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
|
||||
Entry<Variable, Usage> entry = it.next();
|
||||
Usage u = entry.getValue();
|
||||
|
||||
// if no assigns => remove
|
||||
if (u.getAssigns().isEmpty()) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if variable can be declared at current assigns
|
||||
for (IRegion assignRegion : u.getAssigns()) {
|
||||
if (u.getArgRegion() == assignRegion
|
||||
&& canDeclareInRegion(u, assignRegion, regionsOrder)) {
|
||||
u.getArg().getParentInsn().add(AFlag.DECLARE_VAR);
|
||||
processVar(u.getArg());
|
||||
it.remove();
|
||||
break;
|
||||
if (declareAtAssign(u)) {
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usageMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// apply
|
||||
for (Entry<Variable, Usage> entry : usageMap.entrySet()) {
|
||||
@@ -226,7 +274,7 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
Usage addToUsageMap(RegisterArg arg, Map<Variable, Usage> usageMap) {
|
||||
private static Usage addToUsageMap(RegisterArg arg, Map<Variable, Usage> usageMap) {
|
||||
Variable varId = new Variable(arg);
|
||||
Usage usage = usageMap.get(varId);
|
||||
if (usage == null) {
|
||||
@@ -247,6 +295,17 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
return usage;
|
||||
}
|
||||
|
||||
private static boolean declareAtAssign(Usage u) {
|
||||
RegisterArg arg = u.getArg();
|
||||
InsnNode parentInsn = arg.getParentInsn();
|
||||
if (!arg.equals(parentInsn.getResult())) {
|
||||
return false;
|
||||
}
|
||||
parentInsn.add(AFlag.DECLARE_VAR);
|
||||
processVar(arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void declareVar(IContainer region, RegisterArg arg) {
|
||||
DeclareVariablesAttr dv = region.get(AType.DECLARE_VARIABLES);
|
||||
if (dv == null) {
|
||||
@@ -262,7 +321,7 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static int calculateOrder(IContainer container, Map<IContainer, Integer> regionsOrder,
|
||||
int id, boolean inc) {
|
||||
int id, boolean inc) {
|
||||
if (!(container instanceof IRegion)) {
|
||||
return id;
|
||||
}
|
||||
@@ -295,12 +354,20 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
LOG.debug("TODO: Not found order for region {} for {}", region, u);
|
||||
return false;
|
||||
}
|
||||
// workaround for declare variables used in several loops
|
||||
if (region instanceof LoopRegion) {
|
||||
for (IRegion r : u.getAssigns()) {
|
||||
if (!RegionUtils.isRegionContainsRegion(region, r)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isAllRegionsAfter(region, pos, u.getAssigns(), regionsOrder)
|
||||
&& isAllRegionsAfter(region, pos, u.getUseRegions(), regionsOrder);
|
||||
}
|
||||
|
||||
private static boolean isAllRegionsAfter(IRegion region, Integer pos,
|
||||
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
|
||||
private static boolean isAllRegionsAfter(IRegion region, int pos,
|
||||
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
|
||||
for (IRegion r : regions) {
|
||||
if (r == region) {
|
||||
continue;
|
||||
@@ -313,7 +380,7 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
if (pos > rPos) {
|
||||
return false;
|
||||
}
|
||||
if (pos.equals(rPos)) {
|
||||
if (pos == rPos) {
|
||||
return isAllRegionsAfterRecursive(region, regions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
@@ -13,12 +14,12 @@ import jadx.core.dex.nodes.Edge;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfInfo;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.LoopRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.SwitchRegion;
|
||||
import jadx.core.dex.regions.SynchronizedRegion;
|
||||
import jadx.core.dex.regions.conditions.IfInfo;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
@@ -40,8 +41,10 @@ import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.dex.visitors.regions.IfMakerHelper.confirmMerge;
|
||||
import static jadx.core.dex.visitors.regions.IfMakerHelper.makeIfInfo;
|
||||
import static jadx.core.dex.visitors.regions.IfMakerHelper.mergeNestedIfNodes;
|
||||
import static jadx.core.dex.visitors.regions.IfMakerHelper.searchNestedIf;
|
||||
import static jadx.core.utils.BlockUtils.getBlockByOffset;
|
||||
import static jadx.core.utils.BlockUtils.getNextBlock;
|
||||
import static jadx.core.utils.BlockUtils.isPathExists;
|
||||
@@ -67,7 +70,7 @@ public class RegionMaker {
|
||||
if (Consts.DEBUG) {
|
||||
int id = startBlock.getId();
|
||||
if (processedBlocks.get(id)) {
|
||||
LOG.debug(" Block already processed: " + startBlock + ", mth: " + mth);
|
||||
LOG.debug(" Block already processed: {}, mth: {}", startBlock, mth);
|
||||
} else {
|
||||
processedBlocks.set(id);
|
||||
}
|
||||
@@ -137,9 +140,8 @@ public class RegionMaker {
|
||||
}
|
||||
if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
|
||||
return next;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) {
|
||||
@@ -163,17 +165,17 @@ public class RegionMaker {
|
||||
|
||||
LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
|
||||
if (loopRegion == null) {
|
||||
return makeEndlessLoop(curRegion, stack, loop, loopStart);
|
||||
BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);
|
||||
insertContinue(loop);
|
||||
return exit;
|
||||
}
|
||||
curRegion.getSubBlocks().add(loopRegion);
|
||||
IRegion outerRegion = stack.peekRegion();
|
||||
stack.push(loopRegion);
|
||||
|
||||
IfInfo info = makeIfInfo(loopRegion.getHeader());
|
||||
IfInfo condInfo = mergeNestedIfNodes(info);
|
||||
if (condInfo == null) {
|
||||
condInfo = info;
|
||||
}
|
||||
IfInfo condInfo = makeIfInfo(loopRegion.getHeader());
|
||||
condInfo = searchNestedIf(condInfo);
|
||||
confirmMerge(condInfo);
|
||||
if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) {
|
||||
// invert loop condition if 'then' points to exit
|
||||
condInfo = IfInfo.invert(condInfo);
|
||||
@@ -181,15 +183,15 @@ public class RegionMaker {
|
||||
loopRegion.setCondition(condInfo.getCondition());
|
||||
exitBlocks.removeAll(condInfo.getMergedBlocks());
|
||||
|
||||
if (exitBlocks.size() > 0) {
|
||||
if (!exitBlocks.isEmpty()) {
|
||||
BlockNode loopExit = condInfo.getElseBlock();
|
||||
if (loopExit != null) {
|
||||
// add 'break' instruction before path cross between main loop exit and subexit
|
||||
// add 'break' instruction before path cross between main loop exit and sub-exit
|
||||
for (Edge exitEdge : loop.getExitEdges()) {
|
||||
if (!exitBlocks.contains(exitEdge.getSource())) {
|
||||
continue;
|
||||
}
|
||||
tryInsertBreak(stack, loopExit, exitEdge);
|
||||
insertBreak(stack, loopExit, exitEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,6 +234,7 @@ public class RegionMaker {
|
||||
loopRegion.setBody(body);
|
||||
}
|
||||
stack.pop();
|
||||
insertContinue(loop);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -245,7 +248,12 @@ public class RegionMaker {
|
||||
|| block.getInstructions().get(0).getType() != InsnType.IF) {
|
||||
continue;
|
||||
}
|
||||
LoopRegion loopRegion = new LoopRegion(curRegion, block, block == loop.getEnd());
|
||||
List<LoopInfo> loops = block.getAll(AType.LOOP);
|
||||
if (!loops.isEmpty() && loops.get(0) != loop) {
|
||||
// skip nested loop condition
|
||||
continue;
|
||||
}
|
||||
LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd());
|
||||
boolean found;
|
||||
if (block == loop.getStart() || block == loop.getEnd()
|
||||
|| BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
|
||||
@@ -257,6 +265,25 @@ public class RegionMaker {
|
||||
} else {
|
||||
found = false;
|
||||
}
|
||||
if (found) {
|
||||
List<LoopInfo> list = mth.getAllLoopsForBlock(block);
|
||||
if (list.size() >= 2) {
|
||||
// bad condition if successors going out of all loops
|
||||
boolean allOuter = true;
|
||||
for (BlockNode outerBlock : block.getCleanSuccessors()) {
|
||||
List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);
|
||||
outLoopList.remove(loop);
|
||||
if (!outLoopList.isEmpty()) {
|
||||
// goes to outer loop
|
||||
allOuter = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allOuter) {
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return loopRegion;
|
||||
}
|
||||
@@ -266,7 +293,7 @@ public class RegionMaker {
|
||||
}
|
||||
|
||||
private BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) {
|
||||
LoopRegion loopRegion = new LoopRegion(curRegion, null, false);
|
||||
LoopRegion loopRegion = new LoopRegion(curRegion, loop, null, false);
|
||||
curRegion.getSubBlocks().add(loopRegion);
|
||||
|
||||
loopStart.remove(AType.LOOP);
|
||||
@@ -275,16 +302,13 @@ public class RegionMaker {
|
||||
BlockNode loopExit = null;
|
||||
// insert 'break' for exits
|
||||
List<Edge> exitEdges = loop.getExitEdges();
|
||||
if (exitEdges.size() == 1) {
|
||||
for (Edge exitEdge : exitEdges) {
|
||||
BlockNode exit = exitEdge.getTarget();
|
||||
if (canInsertBreak(exit)) {
|
||||
exit.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
|
||||
BlockNode nextBlock = getNextBlock(exit);
|
||||
if (nextBlock != null) {
|
||||
stack.addExit(nextBlock);
|
||||
loopExit = nextBlock;
|
||||
}
|
||||
for (Edge exitEdge : exitEdges) {
|
||||
BlockNode exit = exitEdge.getTarget();
|
||||
if (insertBreak(stack, exit, exitEdge)) {
|
||||
BlockNode nextBlock = getNextBlock(exit);
|
||||
if (nextBlock != null) {
|
||||
stack.addExit(nextBlock);
|
||||
loopExit = nextBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,7 +331,8 @@ public class RegionMaker {
|
||||
}
|
||||
|
||||
private boolean canInsertBreak(BlockNode exit) {
|
||||
if (exit.contains(AFlag.RETURN)) {
|
||||
if (exit.contains(AFlag.RETURN)
|
||||
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
|
||||
return false;
|
||||
}
|
||||
List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
|
||||
@@ -325,21 +350,136 @@ public class RegionMaker {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void tryInsertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
|
||||
BlockNode prev = null;
|
||||
private boolean insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
|
||||
BlockNode exit = exitEdge.getTarget();
|
||||
while (exit != null) {
|
||||
if (prev != null && isPathExists(loopExit, exit)) {
|
||||
// found cross
|
||||
if (canInsertBreak(exit)) {
|
||||
prev.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
|
||||
stack.addExit(exit);
|
||||
BlockNode insertBlock = null;
|
||||
boolean confirm = false;
|
||||
// process special cases
|
||||
if (loopExit == exit) {
|
||||
// try/catch at loop end
|
||||
BlockNode source = exitEdge.getSource();
|
||||
if (source.contains(AType.CATCH_BLOCK)
|
||||
&& source.getSuccessors().size() == 2) {
|
||||
BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
|
||||
if (other != null) {
|
||||
other = BlockUtils.skipSyntheticSuccessor(other);
|
||||
if (other.contains(AType.EXC_HANDLER)) {
|
||||
insertBlock = source;
|
||||
confirm = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = exit;
|
||||
exit = getNextBlock(exit);
|
||||
}
|
||||
if (!confirm) {
|
||||
while (exit != null) {
|
||||
if (insertBlock != null && isPathExists(loopExit, exit)) {
|
||||
// found cross
|
||||
if (canInsertBreak(insertBlock)) {
|
||||
confirm = true;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
insertBlock = exit;
|
||||
List<BlockNode> cs = exit.getCleanSuccessors();
|
||||
exit = cs.size() == 1 ? cs.get(0) : null;
|
||||
}
|
||||
}
|
||||
if (!confirm) {
|
||||
return false;
|
||||
}
|
||||
InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);
|
||||
insertBlock.getInstructions().add(breakInsn);
|
||||
stack.addExit(exit);
|
||||
// add label to 'break' if needed
|
||||
addBreakLabel(exitEdge, exit, breakInsn);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addBreakLabel(Edge exitEdge, BlockNode exit, InsnNode breakInsn) {
|
||||
BlockNode outBlock = BlockUtils.getNextBlock(exitEdge.getTarget());
|
||||
if (outBlock == null) {
|
||||
return;
|
||||
}
|
||||
List<LoopInfo> exitLoop = mth.getAllLoopsForBlock(outBlock);
|
||||
if (!exitLoop.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<LoopInfo> inLoops = mth.getAllLoopsForBlock(exitEdge.getSource());
|
||||
if (inLoops.size() < 2) {
|
||||
return;
|
||||
}
|
||||
// search for parent loop
|
||||
LoopInfo parentLoop = null;
|
||||
for (LoopInfo loop : inLoops) {
|
||||
if (loop.getParentLoop() == null) {
|
||||
parentLoop = loop;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parentLoop == null) {
|
||||
return;
|
||||
}
|
||||
if (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) {
|
||||
LoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop);
|
||||
breakInsn.addAttr(labelAttr);
|
||||
parentLoop.getStart().addAttr(labelAttr);
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertContinue(LoopInfo loop) {
|
||||
BlockNode loopEnd = loop.getEnd();
|
||||
List<BlockNode> predecessors = loopEnd.getPredecessors();
|
||||
if (predecessors.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
Set<BlockNode> loopExitNodes = loop.getExitNodes();
|
||||
for (BlockNode pred : predecessors) {
|
||||
if (canInsertContinue(pred, predecessors, loopEnd, loopExitNodes)) {
|
||||
InsnNode cont = new InsnNode(InsnType.CONTINUE, 0);
|
||||
pred.getInstructions().add(cont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canInsertContinue(BlockNode pred, List<BlockNode> predecessors, BlockNode loopEnd,
|
||||
Set<BlockNode> loopExitNodes) {
|
||||
if (!pred.contains(AFlag.SYNTHETIC)
|
||||
|| BlockUtils.checkLastInsnType(pred, InsnType.CONTINUE)) {
|
||||
return false;
|
||||
}
|
||||
List<BlockNode> preds = pred.getPredecessors();
|
||||
if (preds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
BlockNode codePred = preds.get(0);
|
||||
if (codePred.contains(AFlag.SKIP)) {
|
||||
return false;
|
||||
}
|
||||
if (loopEnd.isDominator(codePred)
|
||||
|| loopExitNodes.contains(codePred)) {
|
||||
return false;
|
||||
}
|
||||
if (isDominatedOnBlocks(codePred, predecessors)) {
|
||||
return false;
|
||||
}
|
||||
boolean gotoExit = false;
|
||||
for (BlockNode exit : loopExitNodes) {
|
||||
if (BlockUtils.isPathExists(codePred, exit)) {
|
||||
gotoExit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return gotoExit;
|
||||
}
|
||||
|
||||
private static boolean isDominatedOnBlocks(BlockNode dom, List<BlockNode> blocks) {
|
||||
for (BlockNode node : blocks) {
|
||||
if (!node.isDominator(dom)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
|
||||
@@ -359,8 +499,7 @@ public class RegionMaker {
|
||||
|
||||
BlockNode body = getNextBlock(block);
|
||||
if (body == null) {
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
LOG.warn("Unexpected end of synchronized block");
|
||||
ErrorsCounter.methodError(mth, "Unexpected end of synchronized block");
|
||||
return null;
|
||||
}
|
||||
BlockNode exit;
|
||||
@@ -382,7 +521,7 @@ public class RegionMaker {
|
||||
* Traverse from monitor-enter thru successors and collect blocks contains monitor-exit
|
||||
*/
|
||||
private static void traverseMonitorExits(SynchronizedRegion region, InsnArg arg, BlockNode block,
|
||||
Set<BlockNode> exits, Set<BlockNode> visited) {
|
||||
Set<BlockNode> exits, Set<BlockNode> visited) {
|
||||
visited.add(block);
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (insn.getType() == InsnType.MONITOR_EXIT
|
||||
@@ -440,9 +579,13 @@ public class RegionMaker {
|
||||
// invert simple condition (compiler often do it)
|
||||
currentIf = IfInfo.invert(currentIf);
|
||||
}
|
||||
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
||||
if (currentIf == null) {
|
||||
// invalid merged if, check simple one again
|
||||
IfInfo modifiedIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
||||
if (modifiedIf != null) {
|
||||
currentIf = modifiedIf;
|
||||
} else {
|
||||
if (currentIf.getMergedBlocks().size() <= 1) {
|
||||
return null;
|
||||
}
|
||||
currentIf = makeIfInfo(block);
|
||||
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
||||
if (currentIf == null) {
|
||||
@@ -450,6 +593,7 @@ public class RegionMaker {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
confirmMerge(currentIf);
|
||||
|
||||
IfRegion ifRegion = new IfRegion(currentRegion, block);
|
||||
ifRegion.setCondition(currentIf.getCondition());
|
||||
@@ -489,7 +633,7 @@ public class RegionMaker {
|
||||
}
|
||||
|
||||
Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len);
|
||||
for (Entry<Integer, List<Object>> entry : casesMap.entrySet()) {
|
||||
for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) {
|
||||
BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors());
|
||||
assert c != null;
|
||||
blocksMap.put(c, entry.getValue());
|
||||
@@ -626,15 +770,6 @@ public class RegionMaker {
|
||||
handler.getHandlerRegion().addAttr(excHandlerAttr);
|
||||
}
|
||||
|
||||
static void skipSimplePath(BlockNode block) {
|
||||
while (block != null
|
||||
&& block.getCleanSuccessors().size() < 2
|
||||
&& block.getPredecessors().size() == 1) {
|
||||
block.add(AFlag.SKIP);
|
||||
block = getNextBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isEqualPaths(BlockNode b1, BlockNode b2) {
|
||||
if (b1 == b2) {
|
||||
return true;
|
||||
|
||||
@@ -4,9 +4,9 @@ import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.LoopRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.SynchronizedRegion;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
@@ -8,9 +8,10 @@ import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.LoopRegion;
|
||||
import jadx.core.dex.regions.SwitchRegion;
|
||||
import jadx.core.dex.regions.TryCatchRegion;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -72,7 +73,8 @@ public class ReturnVisitor extends AbstractVisitor {
|
||||
for (IRegion region : regionStack) {
|
||||
// ignore paths on other branches
|
||||
if (region instanceof IfRegion
|
||||
|| region instanceof SwitchRegion) {
|
||||
|| region instanceof SwitchRegion
|
||||
|| region instanceof TryCatchRegion) {
|
||||
curContainer = region;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2,56 +2,84 @@ package jadx.core.dex.visitors.regions;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.IfRegion;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.TernaryRegion;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.utils.InsnList;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TernaryMod {
|
||||
|
||||
private TernaryMod() {
|
||||
}
|
||||
|
||||
static void makeTernaryInsn(MethodNode mth, IfRegion ifRegion) {
|
||||
static boolean makeTernaryInsn(MethodNode mth, IfRegion ifRegion) {
|
||||
if (ifRegion.contains(AFlag.ELSE_IF_CHAIN)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
IContainer thenRegion = ifRegion.getThenRegion();
|
||||
IContainer elseRegion = ifRegion.getElseRegion();
|
||||
if (thenRegion == null || elseRegion == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
BlockNode tb = getTernaryInsnBlock(thenRegion);
|
||||
BlockNode eb = getTernaryInsnBlock(elseRegion);
|
||||
if (tb == null || eb == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
BlockNode header = ifRegion.getHeader();
|
||||
InsnNode t = tb.getInstructions().get(0);
|
||||
InsnNode e = eb.getInstructions().get(0);
|
||||
|
||||
if (t.getResult() != null && e.getResult() != null
|
||||
&& t.getResult().equalRegisterAndType(e.getResult())
|
||||
&& t.getResult().getSVar().isUsedInPhi()) {
|
||||
if (t.getSourceLine() != e.getSourceLine()) {
|
||||
if (t.getSourceLine() != 0 && e.getSourceLine() != 0) {
|
||||
// sometimes source lines incorrect
|
||||
if (!checkLineStats(t, e)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// no debug info
|
||||
if (containsTernary(t) || containsTernary(e)) {
|
||||
// don't make nested ternary by default
|
||||
// TODO: add addition checks
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t.getResult() != null && e.getResult() != null) {
|
||||
PhiInsn phi = t.getResult().getSVar().getUsedInPhi();
|
||||
if (phi == null || !t.getResult().equalRegisterAndType(e.getResult())) {
|
||||
return false;
|
||||
}
|
||||
if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {
|
||||
return false;
|
||||
}
|
||||
InsnList.remove(tb, t);
|
||||
InsnList.remove(eb, e);
|
||||
|
||||
RegisterArg resArg = t.getResult().getSVar().getUsedInPhi().getResult();
|
||||
RegisterArg resArg;
|
||||
if (phi.getArgsCount() == 2) {
|
||||
resArg = phi.getResult();
|
||||
} else {
|
||||
resArg = t.getResult();
|
||||
phi.removeArg(e.getResult());
|
||||
}
|
||||
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
|
||||
resArg, InsnArg.wrapArg(t), InsnArg.wrapArg(e));
|
||||
ternInsn.setSourceLine(t.getSourceLine());
|
||||
TernaryRegion tern = new TernaryRegion(ifRegion, header);
|
||||
// TODO: add api for replace regions
|
||||
ifRegion.setTernRegion(tern);
|
||||
|
||||
// remove 'if' instruction
|
||||
header.getInstructions().clear();
|
||||
@@ -59,11 +87,15 @@ public class TernaryMod {
|
||||
|
||||
// shrink method again
|
||||
CodeShrinker.shrinkMethod(mth);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mth.getReturnType().equals(ArgType.VOID)
|
||||
&& t.getType() == InsnType.RETURN && e.getType() == InsnType.RETURN) {
|
||||
|
||||
if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {
|
||||
return false;
|
||||
}
|
||||
InsnList.remove(tb, t);
|
||||
InsnList.remove(eb, e);
|
||||
tb.remove(AFlag.RETURN);
|
||||
@@ -78,10 +110,10 @@ public class TernaryMod {
|
||||
header.getInstructions().add(retInsn);
|
||||
header.add(AFlag.RETURN);
|
||||
|
||||
ifRegion.setTernRegion(new TernaryRegion(ifRegion, header));
|
||||
|
||||
CodeShrinker.shrinkMethod(mth);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static BlockNode getTernaryInsnBlock(IContainer thenRegion) {
|
||||
@@ -99,4 +131,59 @@ public class TernaryMod {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean containsTernary(InsnNode insn) {
|
||||
if (insn.getType() == InsnType.TERNARY) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < insn.getArgsCount(); i++) {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
if (arg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (containsTernary(wrapInsn)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 'true' if there are several args with same source lines
|
||||
*/
|
||||
private static boolean checkLineStats(InsnNode t, InsnNode e) {
|
||||
if (t.getResult() == null || e.getResult() == null) {
|
||||
return false;
|
||||
}
|
||||
PhiInsn tPhi = t.getResult().getSVar().getUsedInPhi();
|
||||
PhiInsn ePhi = e.getResult().getSVar().getUsedInPhi();
|
||||
if (tPhi == null || ePhi == null || tPhi != ePhi) {
|
||||
return false;
|
||||
}
|
||||
Map<Integer, Integer> map = new HashMap<Integer, Integer>(tPhi.getArgsCount());
|
||||
for (InsnArg arg : tPhi.getArguments()) {
|
||||
if (!arg.isRegister()) {
|
||||
continue;
|
||||
}
|
||||
InsnNode assignInsn = ((RegisterArg) arg).getAssignInsn();
|
||||
if (assignInsn == null) {
|
||||
continue;
|
||||
}
|
||||
int sourceLine = assignInsn.getSourceLine();
|
||||
if (sourceLine != 0) {
|
||||
Integer count = map.get(sourceLine);
|
||||
if (count != null) {
|
||||
map.put(sourceLine, count + 1);
|
||||
} else {
|
||||
map.put(sourceLine, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() >= 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,12 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class EliminatePhiNodes extends AbstractVisitor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EliminatePhiNodes.class);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.isNoCode()) {
|
||||
@@ -29,13 +34,20 @@ public class EliminatePhiNodes extends AbstractVisitor {
|
||||
}
|
||||
List<PhiInsn> list = phiList.getList();
|
||||
for (PhiInsn phiInsn : list) {
|
||||
for (Iterator<InsnNode> iterator = block.getInstructions().iterator(); iterator.hasNext(); ) {
|
||||
InsnNode insn = iterator.next();
|
||||
if (insn == phiInsn) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
removeInsn(mth, block, phiInsn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeInsn(MethodNode mth, BlockNode block, PhiInsn phiInsn) {
|
||||
Iterator<InsnNode> it = block.getInstructions().iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnNode insn = it.next();
|
||||
if (insn == phiInsn) {
|
||||
it.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG.warn("Phi node not removed: {}, mth: {}", phiInsn, mth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.visitors.ssa;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
@@ -40,7 +41,10 @@ public class SSATransform extends AbstractVisitor {
|
||||
placePhi(mth, i, la);
|
||||
}
|
||||
renameVariables(mth);
|
||||
removeUselessPhi(mth);
|
||||
fixLastTryCatchAssign(mth);
|
||||
if (removeUselessPhi(mth)) {
|
||||
renameVariables(mth);
|
||||
}
|
||||
}
|
||||
|
||||
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
|
||||
@@ -80,6 +84,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
}
|
||||
PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size());
|
||||
phiList.getList().add(phiInsn);
|
||||
phiInsn.setOffset(block.getStartOffset());
|
||||
block.getInstructions().add(0, phiInsn);
|
||||
}
|
||||
|
||||
@@ -126,6 +131,9 @@ public class SSATransform extends AbstractVisitor {
|
||||
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
|
||||
}
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
if (j >= phiInsn.getArgsCount()) {
|
||||
continue;
|
||||
}
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
@@ -143,7 +151,27 @@ public class SSATransform extends AbstractVisitor {
|
||||
System.arraycopy(inputVars, 0, vars, 0, vars.length);
|
||||
}
|
||||
|
||||
private static void removeUselessPhi(MethodNode mth) {
|
||||
private static void fixLastTryCatchAssign(MethodNode mth) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null || !block.contains(AType.EXC_HANDLER)) {
|
||||
continue;
|
||||
}
|
||||
for (PhiInsn phi : phiList.getList()) {
|
||||
for (int i = 0; i < phi.getArgsCount(); i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
InsnNode parentInsn = arg.getAssignInsn();
|
||||
if (parentInsn != null
|
||||
&& parentInsn.getResult() != null
|
||||
&& parentInsn.contains(AFlag.TRY_LEAVE)) {
|
||||
phi.removeArg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean removeUselessPhi(MethodNode mth) {
|
||||
List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
|
||||
for (SSAVar var : mth.getSVars()) {
|
||||
// phi result not used
|
||||
@@ -163,7 +191,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
removePhiWithSameArgs(phi, insnToRemove);
|
||||
}
|
||||
}
|
||||
removePhiList(mth, insnToRemove);
|
||||
return removePhiList(mth, insnToRemove);
|
||||
}
|
||||
|
||||
private static void removePhiWithSameArgs(PhiInsn phi, List<PhiInsn> insnToRemove) {
|
||||
@@ -190,9 +218,9 @@ public class SSATransform extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
|
||||
private static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
|
||||
if (insnToRemove.isEmpty()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
@@ -213,5 +241,6 @@ public class SSATransform extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
insnToRemove.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+24
-13
@@ -3,6 +3,7 @@ package jadx.core.dex.visitors.typeinference;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -15,6 +16,9 @@ import java.util.List;
|
||||
|
||||
public class PostTypeInference {
|
||||
|
||||
private PostTypeInference() {
|
||||
}
|
||||
|
||||
public static boolean process(MethodNode mth, InsnNode insn) {
|
||||
switch (insn.getType()) {
|
||||
case CONST:
|
||||
@@ -24,7 +28,7 @@ public class PostTypeInference {
|
||||
long lit = litArg.getLiteral();
|
||||
if (lit != 0) {
|
||||
// incorrect literal value for object
|
||||
ArgType type = (lit == 1 ? ArgType.BOOLEAN : ArgType.INT);
|
||||
ArgType type = lit == 1 ? ArgType.BOOLEAN : ArgType.INT;
|
||||
// can't merge with object -> force it
|
||||
litArg.setType(type);
|
||||
res.getSVar().setType(type);
|
||||
@@ -84,29 +88,36 @@ public class PostTypeInference {
|
||||
|
||||
case CHECK_CAST: {
|
||||
ArgType castType = (ArgType) ((IndexInsnNode) insn).getIndex();
|
||||
SSAVar sVar = insn.getResult().getSVar();
|
||||
RegisterArg result = insn.getResult();
|
||||
// don't override generic types of same base class
|
||||
boolean skip = castType.isObject() && castType.getObject().equals(sVar.getType().getObject());
|
||||
boolean skip = castType.isObject() && castType.getObject().equals(result.getType().getObject());
|
||||
if (!skip) {
|
||||
// workaround for compiler bug (see TestDuplicateCast)
|
||||
sVar.setType(castType);
|
||||
result.getSVar().setType(castType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case PHI: {
|
||||
PhiInsn phi = (PhiInsn) insn;
|
||||
RegisterArg result = phi.getResult();
|
||||
SSAVar resultSVar = result.getSVar();
|
||||
if (resultSVar != null && !result.getType().isTypeKnown()) {
|
||||
for (InsnArg arg : phi.getArguments()) {
|
||||
ArgType argType = arg.getType();
|
||||
if (argType.isTypeKnown()) {
|
||||
resultSVar.setType(argType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static void setType(InsnArg arg, ArgType type) {
|
||||
if (arg.isRegister()) {
|
||||
((RegisterArg) arg).getSVar().setType(type);
|
||||
} else {
|
||||
arg.setType(type);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean fixArrayTypes(InsnArg array, InsnArg elem) {
|
||||
|
||||
@@ -6,6 +6,9 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class SelectTypeVisitor {
|
||||
|
||||
private SelectTypeVisitor() {
|
||||
}
|
||||
|
||||
public static void visit(InsnNode insn) {
|
||||
InsnArg res = insn.getResult();
|
||||
if (res != null && !res.getType().isTypeKnown()) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.dex.visitors.typeinference;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -9,7 +8,6 @@ import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,45 +28,36 @@ public class TypeInference extends AbstractVisitor {
|
||||
|
||||
// search variable name
|
||||
String name = processVarName(var);
|
||||
if (name != null) {
|
||||
var.setName(name);
|
||||
}
|
||||
var.setName(name);
|
||||
}
|
||||
|
||||
// fix type for vars used only in Phi nodes
|
||||
for (SSAVar sVar : mth.getSVars()) {
|
||||
if (sVar.isUsedInPhi()) {
|
||||
processPhiNode(sVar.getUsedInPhi());
|
||||
PhiInsn phi = sVar.getUsedInPhi();
|
||||
if (phi != null) {
|
||||
processPhiNode(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArgType processType(SSAVar var) {
|
||||
private static ArgType processType(SSAVar var) {
|
||||
RegisterArg assign = var.getAssign();
|
||||
List<RegisterArg> useList = var.getUseList();
|
||||
if (assign != null
|
||||
&& (useList.isEmpty() || assign.isTypeImmutable())) {
|
||||
if (useList.isEmpty() || var.isTypeImmutable()) {
|
||||
return assign.getType();
|
||||
}
|
||||
ArgType type;
|
||||
if (assign != null) {
|
||||
type = assign.getType();
|
||||
} else {
|
||||
type = ArgType.UNKNOWN;
|
||||
}
|
||||
ArgType type = assign.getType();
|
||||
for (RegisterArg arg : useList) {
|
||||
ArgType useType = arg.getType();
|
||||
if (useType.isTypeKnown()) {
|
||||
type = ArgType.merge(type, useType);
|
||||
}
|
||||
if (arg.getParentInsn().contains(AFlag.INCONSISTENT_CODE)) {
|
||||
throw new JadxRuntimeException("not removed arg");
|
||||
ArgType newType = ArgType.merge(type, useType);
|
||||
if (newType != null) {
|
||||
type = newType;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private void processPhiNode(PhiInsn phi) {
|
||||
private static void processPhiNode(PhiInsn phi) {
|
||||
ArgType type = phi.getResult().getType();
|
||||
if (!type.isTypeKnown()) {
|
||||
for (InsnArg arg : phi.getArguments()) {
|
||||
@@ -86,20 +75,17 @@ public class TypeInference extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private String processVarName(SSAVar var) {
|
||||
String name = null;
|
||||
if (var.getAssign() != null) {
|
||||
name = var.getAssign().getName();
|
||||
}
|
||||
private static String processVarName(SSAVar var) {
|
||||
String name = var.getAssign().getName();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
for (RegisterArg arg : var.getUseList()) {
|
||||
String vName = arg.getName();
|
||||
if (vName != null) {
|
||||
name = vName;
|
||||
return vName;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.conditions.IfCondition;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -113,7 +121,12 @@ public class BlockUtils {
|
||||
}
|
||||
|
||||
public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) {
|
||||
assert insn != null;
|
||||
if (insn instanceof PhiInsn) {
|
||||
return searchBlockWithPhi(mth, (PhiInsn) insn);
|
||||
}
|
||||
if (insn.contains(AFlag.WRAPPED)) {
|
||||
return getBlockByWrappedInsn(mth, insn);
|
||||
}
|
||||
for (BlockNode bn : mth.getBasicBlocks()) {
|
||||
if (blockContains(bn, insn)) {
|
||||
return bn;
|
||||
@@ -122,6 +135,87 @@ public class BlockUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BlockNode searchBlockWithPhi(MethodNode mth, PhiInsn insn) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiListAttr = block.get(AType.PHI_LIST);
|
||||
if (phiListAttr != null) {
|
||||
for (PhiInsn phiInsn : phiListAttr.getList()) {
|
||||
if (phiInsn == insn) {
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BlockNode getBlockByWrappedInsn(MethodNode mth, InsnNode insn) {
|
||||
for (BlockNode bn : mth.getBasicBlocks()) {
|
||||
for (InsnNode bi : bn.getInstructions()) {
|
||||
if (bi == insn || foundWrappedInsn(bi, insn) != null) {
|
||||
return bn;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InsnNode searchInsnParent(MethodNode mth, InsnNode insn) {
|
||||
InsnArg insnArg = searchWrappedInsnParent(mth, insn);
|
||||
if (insnArg == null) {
|
||||
return null;
|
||||
}
|
||||
return insnArg.getParentInsn();
|
||||
}
|
||||
|
||||
public static InsnArg searchWrappedInsnParent(MethodNode mth, InsnNode insn) {
|
||||
if (!insn.contains(AFlag.WRAPPED)) {
|
||||
return null;
|
||||
}
|
||||
for (BlockNode bn : mth.getBasicBlocks()) {
|
||||
for (InsnNode bi : bn.getInstructions()) {
|
||||
InsnArg res = foundWrappedInsn(bi, insn);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static InsnArg foundWrappedInsn(InsnNode container, InsnNode insn) {
|
||||
for (InsnArg arg : container.getArguments()) {
|
||||
if (arg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (wrapInsn == insn) {
|
||||
return arg;
|
||||
}
|
||||
InsnArg res = foundWrappedInsn(wrapInsn, insn);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (container instanceof TernaryInsn) {
|
||||
return foundWrappedInsnInCondition(((TernaryInsn) container).getCondition(), insn);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static InsnArg foundWrappedInsnInCondition(IfCondition cond, InsnNode insn) {
|
||||
if (cond.isCompare()) {
|
||||
IfNode cmpInsn = cond.getCompare().getInsn();
|
||||
return foundWrappedInsn(cmpInsn, insn);
|
||||
}
|
||||
for (IfCondition nestedCond : cond.getArgs()) {
|
||||
InsnArg res = foundWrappedInsnInCondition(nestedCond, insn);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BitSet blocksToBitSet(MethodNode mth, List<BlockNode> blocks) {
|
||||
BitSet bs = new BitSet(mth.getBasicBlocks().size());
|
||||
for (BlockNode block : blocks) {
|
||||
@@ -171,16 +265,16 @@ public class BlockUtils {
|
||||
Set<BlockNode> set = new HashSet<BlockNode>();
|
||||
set.add(start);
|
||||
if (start != end) {
|
||||
addPredcessors(set, end, start);
|
||||
addPredecessors(set, end, start);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
private static void addPredcessors(Set<BlockNode> set, BlockNode from, BlockNode until) {
|
||||
private static void addPredecessors(Set<BlockNode> set, BlockNode from, BlockNode until) {
|
||||
set.add(from);
|
||||
for (BlockNode pred : from.getPredecessors()) {
|
||||
if (pred != until && !set.contains(pred)) {
|
||||
addPredcessors(set, pred, until);
|
||||
addPredecessors(set, pred, until);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,29 +310,6 @@ public class BlockUtils {
|
||||
return traverseSuccessorsUntil(start, end, new BitSet());
|
||||
}
|
||||
|
||||
public static boolean isCleanPathExists(BlockNode start, BlockNode end) {
|
||||
if (start == end || start.getCleanSuccessors().contains(end)) {
|
||||
return true;
|
||||
}
|
||||
return traverseCleanSuccessorsUntil(start, end, new BitSet());
|
||||
}
|
||||
|
||||
private static boolean traverseCleanSuccessorsUntil(BlockNode from, BlockNode until, BitSet visited) {
|
||||
for (BlockNode s : from.getCleanSuccessors()) {
|
||||
if (s == until) {
|
||||
return true;
|
||||
}
|
||||
int id = s.getId();
|
||||
if (!visited.get(id) && !s.contains(AType.EXC_HANDLER)) {
|
||||
visited.set(id);
|
||||
if (traverseSuccessorsUntil(s, until, visited)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isOnlyOnePathExists(BlockNode start, BlockNode end) {
|
||||
if (start == end) {
|
||||
return true;
|
||||
@@ -345,4 +416,14 @@ public class BlockUtils {
|
||||
}
|
||||
return block == end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return successor of synthetic block or same block otherwise.
|
||||
*/
|
||||
public static BlockNode skipSyntheticSuccessor(BlockNode block) {
|
||||
if (block.isSynthetic() && !block.getSuccessors().isEmpty()) {
|
||||
return block.getSuccessors().get(0);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package jadx.core.utils;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class EmptyBitSet extends BitSet {
|
||||
public final class EmptyBitSet extends BitSet {
|
||||
|
||||
private static final long serialVersionUID = -1194884945157778639L;
|
||||
|
||||
public static final BitSet EMPTY = new EmptyBitSet();
|
||||
|
||||
public EmptyBitSet() {
|
||||
super(0);
|
||||
}
|
||||
@@ -62,7 +64,7 @@ public class EmptyBitSet extends BitSet {
|
||||
|
||||
@Override
|
||||
public BitSet get(int fromIndex, int toIndex) {
|
||||
throw new UnsupportedOperationException();
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,4 +86,9 @@ public class EmptyBitSet extends BitSet {
|
||||
public void andNot(BitSet set) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class ErrorsCounter {
|
||||
if (e.getClass() == JadxOverflowException.class) {
|
||||
// don't print full stack trace
|
||||
e = new JadxOverflowException(e.getMessage());
|
||||
LOG.error(msg);
|
||||
LOG.error(msg + ", message: " + e.getMessage());
|
||||
} else {
|
||||
LOG.error(msg, e);
|
||||
}
|
||||
@@ -57,6 +57,10 @@ public class ErrorsCounter {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static String classError(ClassNode mth, String errorMsg) {
|
||||
return classError(mth, errorMsg, null);
|
||||
}
|
||||
|
||||
public static String methodError(MethodNode mth, String errorMsg, Throwable e) {
|
||||
String msg = formatErrorMsg(mth, errorMsg);
|
||||
mth.dex().root().getErrorsCounter().addError(mth, msg, e);
|
||||
@@ -69,7 +73,7 @@ public class ErrorsCounter {
|
||||
|
||||
public void printReport() {
|
||||
if (getErrorCount() > 0) {
|
||||
LOG.error(getErrorCount() + " errors occured in following nodes:");
|
||||
LOG.error("{} errors occurred in following nodes:", getErrorCount());
|
||||
List<Object> nodes = new ArrayList<Object>(errorNodes);
|
||||
Collections.sort(nodes, new Comparator<Object>() {
|
||||
@Override
|
||||
@@ -79,7 +83,7 @@ public class ErrorsCounter {
|
||||
});
|
||||
for (Object node : nodes) {
|
||||
String nodeName = node.getClass().getSimpleName().replace("Node", "");
|
||||
LOG.error(" " + nodeName + ": " + node);
|
||||
LOG.error(" {}: {}", nodeName, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,7 @@ public class InsnUtils {
|
||||
}
|
||||
|
||||
public static String insnTypeToString(InsnType type) {
|
||||
return insnTypeToString(type.toString());
|
||||
}
|
||||
|
||||
public static String insnTypeToString(String str) {
|
||||
return String.format("%s ", str);
|
||||
return type.toString() + " ";
|
||||
}
|
||||
|
||||
public static String indexToString(Object index) {
|
||||
@@ -49,7 +45,7 @@ public class InsnUtils {
|
||||
if (index instanceof String) {
|
||||
return "\"" + index + "\"";
|
||||
} else {
|
||||
return " " + index;
|
||||
return index.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
@@ -66,9 +65,6 @@ public class InstructionRemover {
|
||||
public static void unbindInsn(MethodNode mth, InsnNode insn) {
|
||||
RegisterArg r = insn.getResult();
|
||||
if (r != null && r.getSVar() != null) {
|
||||
if (Consts.DEBUG && r.getSVar().getUseCount() != 0) {
|
||||
LOG.debug("Unbind insn with result: {}", insn);
|
||||
}
|
||||
mth.removeSVar(r.getSVar());
|
||||
}
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
|
||||
@@ -6,6 +6,9 @@ import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IContainer;
|
||||
import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.SwitchRegion;
|
||||
import jadx.core.dex.regions.conditions.IfRegion;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
@@ -24,7 +27,7 @@ public class RegionUtils {
|
||||
public static boolean hasExitEdge(IContainer container) {
|
||||
if (container instanceof BlockNode) {
|
||||
BlockNode block = (BlockNode) container;
|
||||
return block.getSuccessors().size() != 0
|
||||
return !block.getSuccessors().isEmpty()
|
||||
&& !block.contains(AFlag.RETURN);
|
||||
} else if (container instanceof IRegion) {
|
||||
IRegion region = (IRegion) container;
|
||||
@@ -35,6 +38,29 @@ public class RegionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static InsnNode getLastInsn(IContainer container) {
|
||||
if (container instanceof BlockNode) {
|
||||
BlockNode block = (BlockNode) container;
|
||||
List<InsnNode> insnList = block.getInstructions();
|
||||
if (insnList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return insnList.get(insnList.size() - 1);
|
||||
} else if (container instanceof IfRegion
|
||||
|| container instanceof SwitchRegion) {
|
||||
return null;
|
||||
} else if (container instanceof IRegion) {
|
||||
IRegion region = (IRegion) container;
|
||||
List<IContainer> blocks = region.getSubBlocks();
|
||||
if (blocks.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getLastInsn(blocks.get(blocks.size() - 1));
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown container type: " + container.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if last block in region has no successors
|
||||
*/
|
||||
@@ -83,7 +109,7 @@ public class RegionUtils {
|
||||
|
||||
public static boolean notEmpty(IContainer container) {
|
||||
if (container instanceof BlockNode) {
|
||||
return ((BlockNode) container).getInstructions().size() != 0;
|
||||
return !((BlockNode) container).getInstructions().isEmpty();
|
||||
} else if (container instanceof IRegion) {
|
||||
IRegion region = (IRegion) container;
|
||||
for (IContainer block : region.getSubBlocks()) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Iterator;
|
||||
@@ -94,14 +91,7 @@ public class Utils {
|
||||
return sw.getBuffer().toString();
|
||||
}
|
||||
|
||||
public static void makeDirsForFile(File file) {
|
||||
File dir = file.getParentFile();
|
||||
if (dir != null && !dir.exists()) {
|
||||
// if directory already created in other thread mkdirs will return false,
|
||||
// so check dir existence again
|
||||
if (!dir.mkdirs() && !dir.exists()) {
|
||||
throw new JadxRuntimeException("Can't create directory " + dir);
|
||||
}
|
||||
}
|
||||
public static int compare(int x, int y) {
|
||||
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.core.utils.files;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -35,4 +37,15 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeDirsForFile(File file) {
|
||||
File dir = file.getParentFile();
|
||||
if (dir != null && !dir.exists()) {
|
||||
// if directory already created in other thread mkdirs will return false,
|
||||
// so check dir existence again
|
||||
if (!dir.mkdirs() && !dir.exists()) {
|
||||
throw new JadxRuntimeException("Can't create directory " + dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class InputFile {
|
||||
if (ba.length == 0) {
|
||||
throw new JadxException(j2d.isError() ? j2d.getDxErrors() : "Empty dx output");
|
||||
} else if (j2d.isError()) {
|
||||
LOG.warn("dx message: " + j2d.getDxErrors());
|
||||
LOG.warn("dx message: {}", j2d.getDxErrors());
|
||||
}
|
||||
return new Dex(ba);
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package jadx.tests
|
||||
|
||||
import jadx.api.IJadxArgs
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.core.utils.exceptions.JadxException
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user