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
|
language: java
|
||||||
jdk:
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
- oraclejdk7
|
- oraclejdk7
|
||||||
- openjdk7
|
|
||||||
- openjdk6
|
- openjdk6
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
|||||||
@@ -61,6 +61,20 @@ Example:
|
|||||||
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
||||||
`DEFAULT_JVM_OPTS="-Xmx2500M"`
|
`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*
|
*Licensed under the Apache 2.0 License*
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -32,13 +32,14 @@ subprojects {
|
|||||||
|
|
||||||
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
||||||
testCompile 'junit:junit:4.11'
|
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 'org.spockframework:spock-core:0.7-groovy-2.0'
|
||||||
testCompile 'cglib:cglib-nodep:3.1'
|
testCompile 'cglib:cglib-nodep:3.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
jacocoTestReport {
|
jacocoTestReport {
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ public final class JadxCLIArgs implements IJadxArgs {
|
|||||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||||
protected int threadsCount = Runtime.getRuntime().availableProcessors();
|
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;
|
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")
|
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
||||||
protected boolean cfgOutput = false;
|
protected boolean cfgOutput = false;
|
||||||
|
|
||||||
@@ -181,6 +184,11 @@ public final class JadxCLIArgs implements IJadxArgs {
|
|||||||
return fallbackMode;
|
return fallbackMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShowInconsistentCode() {
|
||||||
|
return showInconsistentCode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVerbose() {
|
public boolean isVerbose() {
|
||||||
return verbose;
|
return verbose;
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile files('lib/dx-1.8.jar')
|
|
||||||
compile 'org.ow2.asm:asm:5.0.3'
|
|
||||||
runtime files(jadxClasspath)
|
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) {
|
task packTests(type: Jar) {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -29,6 +29,11 @@ public class DefaultJadxArgs implements IJadxArgs {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShowInconsistentCode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVerbose() {
|
public boolean isVerbose() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ public interface IJadxArgs {
|
|||||||
|
|
||||||
boolean isFallbackMode();
|
boolean isFallbackMode();
|
||||||
|
|
||||||
|
boolean isShowInconsistentCode();
|
||||||
|
|
||||||
boolean isVerbose();
|
boolean isVerbose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,28 +116,31 @@ public final class JavaClass implements JavaNode {
|
|||||||
public CodePosition getDefinitionPosition(int line, int offset) {
|
public CodePosition getDefinitionPosition(int line, int offset) {
|
||||||
Map<CodePosition, Object> map = getCodeAnnotations();
|
Map<CodePosition, Object> map = getCodeAnnotations();
|
||||||
Object obj = map.get(new CodePosition(line, offset));
|
Object obj = map.get(new CodePosition(line, offset));
|
||||||
if (obj instanceof LineAttrNode) {
|
if (!(obj instanceof LineAttrNode)) {
|
||||||
ClassNode clsNode = null;
|
return 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);
|
|
||||||
}
|
}
|
||||||
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) {
|
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.SimplifyVisitor;
|
||||||
import jadx.core.dex.visitors.regions.CheckRegions;
|
import jadx.core.dex.visitors.regions.CheckRegions;
|
||||||
import jadx.core.dex.visitors.regions.IfRegionVisitor;
|
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.ProcessVariables;
|
||||||
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
|
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
|
||||||
import jadx.core.dex.visitors.regions.ReturnVisitor;
|
import jadx.core.dex.visitors.regions.ReturnVisitor;
|
||||||
@@ -59,7 +60,7 @@ public class Jadx {
|
|||||||
passes.add(new DebugInfoVisitor());
|
passes.add(new DebugInfoVisitor());
|
||||||
passes.add(new TypeInference());
|
passes.add(new TypeInference());
|
||||||
if (args.isRawCFGOutput()) {
|
if (args.isRawCFGOutput()) {
|
||||||
passes.add(new DotGraphVisitor(outDir, false, true));
|
passes.add(DotGraphVisitor.dumpRaw(outDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
passes.add(new ConstInlinerVisitor());
|
passes.add(new ConstInlinerVisitor());
|
||||||
@@ -72,7 +73,7 @@ public class Jadx {
|
|||||||
passes.add(new CodeShrinker());
|
passes.add(new CodeShrinker());
|
||||||
passes.add(new ReSugarCode());
|
passes.add(new ReSugarCode());
|
||||||
if (args.isCFGOutput()) {
|
if (args.isCFGOutput()) {
|
||||||
passes.add(new DotGraphVisitor(outDir, false));
|
passes.add(DotGraphVisitor.dump(outDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
passes.add(new RegionMakerVisitor());
|
passes.add(new RegionMakerVisitor());
|
||||||
@@ -81,16 +82,17 @@ public class Jadx {
|
|||||||
|
|
||||||
passes.add(new CodeShrinker());
|
passes.add(new CodeShrinker());
|
||||||
passes.add(new SimplifyVisitor());
|
passes.add(new SimplifyVisitor());
|
||||||
passes.add(new ProcessVariables());
|
|
||||||
passes.add(new CheckRegions());
|
passes.add(new CheckRegions());
|
||||||
|
|
||||||
if (args.isCFGOutput()) {
|
if (args.isCFGOutput()) {
|
||||||
passes.add(new DotGraphVisitor(outDir, true));
|
passes.add(DotGraphVisitor.dumpRegions(outDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
passes.add(new MethodInlineVisitor());
|
passes.add(new MethodInlineVisitor());
|
||||||
passes.add(new ClassModifier());
|
passes.add(new ClassModifier());
|
||||||
passes.add(new PrepareForCodeGen());
|
passes.add(new PrepareForCodeGen());
|
||||||
|
passes.add(new LoopRegionVisitor());
|
||||||
|
passes.add(new ProcessVariables());
|
||||||
}
|
}
|
||||||
passes.add(new CodeGen(args));
|
passes.add(new CodeGen(args));
|
||||||
return passes;
|
return passes;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public final class ProcessClass {
|
|||||||
DepthTraversal.visit(visitor, cls);
|
DepthTraversal.visit(visitor, cls);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Class process exception: " + cls, e);
|
LOG.error("Class process exception: {}", cls, e);
|
||||||
} finally {
|
} finally {
|
||||||
cls.unload();
|
cls.unload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package jadx.core.clsp;
|
|||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
import jadx.core.dex.nodes.RootNode;
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
import jadx.core.utils.exceptions.DecodeException;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -96,13 +96,13 @@ public class ClsSet {
|
|||||||
private static NClass getCls(String fullName, Map<String, NClass> names) {
|
private static NClass getCls(String fullName, Map<String, NClass> names) {
|
||||||
NClass id = names.get(fullName);
|
NClass id = names.get(fullName);
|
||||||
if (id == null && !names.containsKey(fullName)) {
|
if (id == null && !names.containsKey(fullName)) {
|
||||||
LOG.warn("Class not found: " + fullName);
|
LOG.warn("Class not found: {}", fullName);
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void save(File output) throws IOException {
|
void save(File output) throws IOException {
|
||||||
Utils.makeDirsForFile(output);
|
FileUtils.makeDirsForFile(output);
|
||||||
|
|
||||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
||||||
try {
|
try {
|
||||||
@@ -132,7 +132,7 @@ public class ClsSet {
|
|||||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||||
out.writeByte(VERSION);
|
out.writeByte(VERSION);
|
||||||
|
|
||||||
LOG.info("Classes count: " + classes.length);
|
LOG.info("Classes count: {}", classes.length);
|
||||||
out.writeInt(classes.length);
|
out.writeInt(classes.length);
|
||||||
for (NClass cls : classes) {
|
for (NClass cls : classes) {
|
||||||
writeString(out, cls.getName());
|
writeString(out, cls.getName());
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class ConvertToClsSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (InputFile inputFile : inputFiles) {
|
for (InputFile inputFile : inputFiles) {
|
||||||
LOG.info("Loaded: " + inputFile.getFile());
|
LOG.info("Loaded: {}", inputFile.getFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
RootNode root = new RootNode();
|
RootNode root = new RootNode();
|
||||||
@@ -48,7 +48,7 @@ public class ConvertToClsSet {
|
|||||||
ClsSet set = new ClsSet();
|
ClsSet set = new ClsSet();
|
||||||
set.load(root);
|
set.load(root);
|
||||||
set.save(output);
|
set.save(output);
|
||||||
LOG.info("Output: " + output);
|
LOG.info("Output: {}", output);
|
||||||
LOG.info("done");
|
LOG.info("done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class AnnotationGen {
|
|||||||
|
|
||||||
public void addForParameter(CodeWriter code, MethodParameters paramsAnnotations, int n) {
|
public void addForParameter(CodeWriter code, MethodParameters paramsAnnotations, int n) {
|
||||||
AnnotationsList aList = paramsAnnotations.getParamList().get(n);
|
AnnotationsList aList = paramsAnnotations.getParamList().get(n);
|
||||||
if (aList == null || aList.size() == 0) {
|
if (aList == null || aList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Annotation a : aList.getAll()) {
|
for (Annotation a : aList.getAll()) {
|
||||||
@@ -54,7 +54,7 @@ public class AnnotationGen {
|
|||||||
|
|
||||||
private void add(IAttributeNode node, CodeWriter code) {
|
private void add(IAttributeNode node, CodeWriter code) {
|
||||||
AnnotationsList aList = node.get(AType.ANNOTATION_LIST);
|
AnnotationsList aList = node.get(AType.ANNOTATION_LIST);
|
||||||
if (aList == null || aList.size() == 0) {
|
if (aList == null || aList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Annotation a : aList.getAll()) {
|
for (Annotation a : aList.getAll()) {
|
||||||
@@ -150,9 +150,9 @@ public class AnnotationGen {
|
|||||||
// must be a static field
|
// must be a static field
|
||||||
FieldInfo field = (FieldInfo) val;
|
FieldInfo field = (FieldInfo) val;
|
||||||
InsnGen.makeStaticFieldAccess(code, field, classGen);
|
InsnGen.makeStaticFieldAccess(code, field, classGen);
|
||||||
} else if (val instanceof List) {
|
} else if (val instanceof Iterable) {
|
||||||
code.add('{');
|
code.add('{');
|
||||||
Iterator<?> it = ((List) val).iterator();
|
Iterator<?> it = ((Iterable) val).iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Object obj = it.next();
|
Object obj = it.next();
|
||||||
encodeValue(code, obj);
|
encodeValue(code, obj);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package jadx.core.codegen;
|
package jadx.core.codegen;
|
||||||
|
|
||||||
|
import jadx.api.IJadxArgs;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
@@ -23,6 +24,7 @@ import jadx.core.utils.exceptions.CodegenException;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,14 +40,28 @@ import com.android.dx.rop.code.AccessFlags;
|
|||||||
public class ClassGen {
|
public class ClassGen {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
|
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 ClassNode cls;
|
||||||
private final ClassGen parentGen;
|
private final ClassGen parentGen;
|
||||||
private final AnnotationGen annotationGen;
|
private final AnnotationGen annotationGen;
|
||||||
private final boolean fallback;
|
private final boolean fallback;
|
||||||
|
|
||||||
|
private boolean showInconsistentCode = false;
|
||||||
|
|
||||||
private final Set<ClassInfo> imports = new HashSet<ClassInfo>();
|
private final Set<ClassInfo> imports = new HashSet<ClassInfo>();
|
||||||
private int clsDeclLine;
|
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) {
|
public ClassGen(ClassNode cls, ClassGen parentClsGen, boolean fallback) {
|
||||||
this.cls = cls;
|
this.cls = cls;
|
||||||
this.parentGen = parentClsGen;
|
this.parentGen = parentClsGen;
|
||||||
@@ -108,6 +124,11 @@ public class ClassGen {
|
|||||||
.remove(AccessFlags.ACC_STATIC);
|
.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);
|
annotationGen.addForClass(clsCode);
|
||||||
insertSourceFileInfo(clsCode, cls);
|
insertSourceFileInfo(clsCode, cls);
|
||||||
clsCode.startLine(af.makeString());
|
clsCode.startLine(af.makeString());
|
||||||
@@ -135,7 +156,7 @@ public class ClassGen {
|
|||||||
clsCode.add(' ');
|
clsCode.add(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) {
|
if (!cls.getInterfaces().isEmpty() && !af.isAnnotation()) {
|
||||||
if (cls.getAccessFlags().isInterface()) {
|
if (cls.getAccessFlags().isInterface()) {
|
||||||
clsCode.add("extends ");
|
clsCode.add("extends ");
|
||||||
} else {
|
} else {
|
||||||
@@ -226,7 +247,8 @@ public class ClassGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addMethods(CodeWriter code) {
|
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)) {
|
if (mth.contains(AFlag.DONT_GENERATE)) {
|
||||||
continue;
|
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() {
|
private boolean isMethodsPresents() {
|
||||||
for (MethodNode mth : cls.getMethods()) {
|
for (MethodNode mth : cls.getMethods()) {
|
||||||
if (!mth.contains(AFlag.DONT_GENERATE)) {
|
if (!mth.contains(AFlag.DONT_GENERATE)) {
|
||||||
@@ -269,6 +297,9 @@ public class ClassGen {
|
|||||||
code.startLine("/* JADX WARNING: inconsistent code. */");
|
code.startLine("/* JADX WARNING: inconsistent code. */");
|
||||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||||
ErrorsCounter.methodError(mth, "Inconsistent code");
|
ErrorsCounter.methodError(mth, "Inconsistent code");
|
||||||
|
if (showInconsistentCode) {
|
||||||
|
mth.remove(AFlag.INCONSISTENT_CODE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MethodGen mthGen;
|
MethodGen mthGen;
|
||||||
if (badCode || mth.contains(AType.JADX_ERROR)) {
|
if (badCode || mth.contains(AType.JADX_ERROR)) {
|
||||||
@@ -331,7 +362,7 @@ public class ClassGen {
|
|||||||
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
|
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
|
||||||
EnumField f = it.next();
|
EnumField f = it.next();
|
||||||
code.startLine(f.getName());
|
code.startLine(f.getName());
|
||||||
if (f.getArgs().size() != 0) {
|
if (!f.getArgs().isEmpty()) {
|
||||||
code.add('(');
|
code.add('(');
|
||||||
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
|
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
|
||||||
InsnArg arg = aIt.next();
|
InsnArg arg = aIt.next();
|
||||||
@@ -387,8 +418,8 @@ public class ClassGen {
|
|||||||
code.attachAnnotation(classNode);
|
code.attachAnnotation(classNode);
|
||||||
}
|
}
|
||||||
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
|
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
|
||||||
ArgType[] generics = classInfo.getType().getGenericTypes();
|
|
||||||
code.add(baseClass);
|
code.add(baseClass);
|
||||||
|
ArgType[] generics = classInfo.getType().getGenericTypes();
|
||||||
if (generics != null) {
|
if (generics != null) {
|
||||||
code.add('<');
|
code.add('<');
|
||||||
int len = generics.length;
|
int len = generics.length;
|
||||||
@@ -435,7 +466,7 @@ public class ClassGen {
|
|||||||
if (classNode != null && !classNode.getAccessFlags().isPublic()) {
|
if (classNode != null && !classNode.getAccessFlags().isPublic()) {
|
||||||
return shortName;
|
return shortName;
|
||||||
}
|
}
|
||||||
if (searchCollision(cls.dex(), useCls, shortName)) {
|
if (searchCollision(cls.dex(), useCls, classInfo)) {
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
if (classInfo.getPackage().equals(useCls.getPackage())) {
|
if (classInfo.getPackage().equals(useCls.getPackage())) {
|
||||||
@@ -481,22 +512,24 @@ public class ClassGen {
|
|||||||
return false;
|
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) {
|
if (useCls == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
String shortName = searchCls.getShortName();
|
||||||
if (useCls.getShortName().equals(shortName)) {
|
if (useCls.getShortName().equals(shortName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ClassNode classNode = dex.resolveClass(useCls);
|
ClassNode classNode = dex.resolveClass(useCls);
|
||||||
if (classNode != null) {
|
if (classNode != null) {
|
||||||
for (ClassNode inner : classNode.getInnerClasses()) {
|
for (ClassNode inner : classNode.getInnerClasses()) {
|
||||||
if (inner.getShortName().equals(shortName)) {
|
if (inner.getShortName().equals(shortName)
|
||||||
|
&& !inner.getClassInfo().equals(searchCls)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return searchCollision(dex, useCls.getParentClass(), shortName);
|
return searchCollision(dex, useCls.getParentClass(), searchCls);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertSourceFileInfo(CodeWriter code, AttrNode node) {
|
private void insertSourceFileInfo(CodeWriter code, AttrNode node) {
|
||||||
|
|||||||
@@ -15,15 +15,11 @@ public class CodeGen extends AbstractVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(ClassNode cls) throws CodegenException {
|
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();
|
CodeWriter clsCode = clsGen.makeClass();
|
||||||
clsCode.finish();
|
clsCode.finish();
|
||||||
cls.setCode(clsCode);
|
cls.setCode(clsCode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFallbackMode() {
|
|
||||||
return args.isFallbackMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package jadx.core.codegen;
|
|||||||
|
|
||||||
import jadx.api.CodePosition;
|
import jadx.api.CodePosition;
|
||||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
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.File;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@@ -22,6 +22,8 @@ public class CodeWriter {
|
|||||||
public static final String NL = System.getProperty("line.separator");
|
public static final String NL = System.getProperty("line.separator");
|
||||||
public static final String INDENT = " ";
|
public static final String INDENT = " ";
|
||||||
|
|
||||||
|
private static final boolean ADD_LINE_NUMBERS = false;
|
||||||
|
|
||||||
private static final String[] INDENT_CACHE = {
|
private static final String[] INDENT_CACHE = {
|
||||||
"",
|
"",
|
||||||
INDENT,
|
INDENT,
|
||||||
@@ -43,6 +45,9 @@ public class CodeWriter {
|
|||||||
public CodeWriter() {
|
public CodeWriter() {
|
||||||
this.indent = 0;
|
this.indent = 0;
|
||||||
this.indentStr = "";
|
this.indentStr = "";
|
||||||
|
if (ADD_LINE_NUMBERS) {
|
||||||
|
incIndent(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeWriter startLine() {
|
public CodeWriter startLine() {
|
||||||
@@ -65,6 +70,26 @@ public class CodeWriter {
|
|||||||
return this;
|
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) {
|
public CodeWriter add(String str) {
|
||||||
buf.append(str);
|
buf.append(str);
|
||||||
offset += str.length();
|
offset += str.length();
|
||||||
@@ -263,11 +288,11 @@ public class CodeWriter {
|
|||||||
|
|
||||||
PrintWriter out = null;
|
PrintWriter out = null;
|
||||||
try {
|
try {
|
||||||
Utils.makeDirsForFile(file);
|
FileUtils.makeDirsForFile(file);
|
||||||
out = new PrintWriter(file, "UTF-8");
|
out = new PrintWriter(file, "UTF-8");
|
||||||
String code = buf.toString();
|
String code = buf.toString();
|
||||||
code = removeFirstEmptyLine(code);
|
code = removeFirstEmptyLine(code);
|
||||||
out.print(code);
|
out.println(code);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Save file error", e);
|
LOG.error("Save file error", e);
|
||||||
} finally {
|
} 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.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.regions.Compare;
|
import jadx.core.dex.regions.conditions.Compare;
|
||||||
import jadx.core.dex.regions.IfCondition;
|
import jadx.core.dex.regions.conditions.IfCondition;
|
||||||
import jadx.core.dex.regions.IfCondition.Mode;
|
import jadx.core.dex.regions.conditions.IfCondition.Mode;
|
||||||
import jadx.core.utils.ErrorsCounter;
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.exceptions.CodegenException;
|
import jadx.core.utils.exceptions.CodegenException;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -23,42 +25,83 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class ConditionGen extends InsnGen {
|
public class ConditionGen extends InsnGen {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ConditionGen.class);
|
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) {
|
public ConditionGen(InsnGen insnGen) {
|
||||||
super(insnGen.mgen, insnGen.fallback);
|
super(insnGen.mgen, insnGen.fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(CodeWriter code, IfCondition condition) throws CodegenException {
|
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()) {
|
switch (condition.getMode()) {
|
||||||
case COMPARE:
|
case COMPARE:
|
||||||
addCompare(code, condition.getCompare());
|
addCompare(code, stack, condition.getCompare());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TERNARY:
|
||||||
|
addTernary(code, stack, condition);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NOT:
|
case NOT:
|
||||||
addNot(code, condition);
|
addNot(code, stack, condition);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AND:
|
case AND:
|
||||||
case OR:
|
case OR:
|
||||||
addAndOr(code, condition);
|
addAndOr(code, stack, condition);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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);
|
boolean wrap = isWrapNeeded(cond);
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
code.add('(');
|
code.add('(');
|
||||||
}
|
}
|
||||||
add(code, cond);
|
add(code, stack, cond);
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
code.add(')');
|
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();
|
IfOp op = compare.getOp();
|
||||||
InsnArg firstArg = compare.getA();
|
InsnArg firstArg = compare.getA();
|
||||||
InsnArg secondArg = compare.getB();
|
InsnArg secondArg = compare.getB();
|
||||||
@@ -71,19 +114,16 @@ public class ConditionGen extends InsnGen {
|
|||||||
}
|
}
|
||||||
if (op == IfOp.EQ) {
|
if (op == IfOp.EQ) {
|
||||||
// == true
|
// == true
|
||||||
addArg(code, firstArg, false);
|
if (stack.getStack().size() == 1) {
|
||||||
|
addArg(code, firstArg, false);
|
||||||
|
} else {
|
||||||
|
wrap(code, firstArg);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else if (op == IfOp.NE) {
|
} else if (op == IfOp.NE) {
|
||||||
// != true
|
// != true
|
||||||
code.add('!');
|
code.add('!');
|
||||||
boolean wrap = isArgWrapNeeded(firstArg);
|
wrap(code, firstArg);
|
||||||
if (wrap) {
|
|
||||||
code.add('(');
|
|
||||||
}
|
|
||||||
addArg(code, firstArg, false);
|
|
||||||
if (wrap) {
|
|
||||||
code.add(')');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
|
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
|
||||||
@@ -94,16 +134,24 @@ public class ConditionGen extends InsnGen {
|
|||||||
addArg(code, secondArg, isArgWrapNeeded(secondArg));
|
addArg(code, secondArg, isArgWrapNeeded(secondArg));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNot(CodeWriter code, IfCondition condition) throws CodegenException {
|
private void addTernary(CodeWriter code, CondStack stack, IfCondition condition) throws CodegenException {
|
||||||
code.add('!');
|
add(code, stack, condition.first());
|
||||||
wrap(code, condition.getArgs().get(0));
|
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 ? " && " : " || ";
|
String mode = condition.getMode() == Mode.AND ? " && " : " || ";
|
||||||
Iterator<IfCondition> it = condition.getArgs().iterator();
|
Iterator<IfCondition> it = condition.getArgs().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
wrap(code, it.next());
|
wrap(code, stack, it.next());
|
||||||
if (it.hasNext()) {
|
if (it.hasNext()) {
|
||||||
code.add(mode);
|
code.add(mode);
|
||||||
}
|
}
|
||||||
@@ -111,7 +159,13 @@ public class ConditionGen extends InsnGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWrapNeeded(IfCondition condition) {
|
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) {
|
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.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
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.attributes.nodes.MethodInlineAttr;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
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.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
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.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||||
@@ -46,7 +47,9 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -58,9 +61,10 @@ public class InsnGen {
|
|||||||
protected final RootNode root;
|
protected final RootNode root;
|
||||||
protected final boolean fallback;
|
protected final boolean fallback;
|
||||||
|
|
||||||
private enum Flags {
|
protected enum Flags {
|
||||||
BODY_ONLY,
|
BODY_ONLY,
|
||||||
BODY_ONLY_NOWRAP,
|
BODY_ONLY_NOWRAP,
|
||||||
|
INLINE
|
||||||
}
|
}
|
||||||
|
|
||||||
public InsnGen(MethodGen mgen, boolean fallback) {
|
public InsnGen(MethodGen mgen, boolean fallback) {
|
||||||
@@ -95,7 +99,7 @@ public class InsnGen {
|
|||||||
Flags flag = wrap ? Flags.BODY_ONLY : Flags.BODY_ONLY_NOWRAP;
|
Flags flag = wrap ? Flags.BODY_ONLY : Flags.BODY_ONLY_NOWRAP;
|
||||||
makeInsn(((InsnWrapArg) arg).getWrapInsn(), code, flag);
|
makeInsn(((InsnWrapArg) arg).getWrapInsn(), code, flag);
|
||||||
} else if (arg.isNamed()) {
|
} else if (arg.isNamed()) {
|
||||||
code.add(((NamedArg) arg).getName());
|
code.add(((Named) arg).getName());
|
||||||
} else if (arg.isField()) {
|
} else if (arg.isField()) {
|
||||||
FieldArg f = (FieldArg) arg;
|
FieldArg f = (FieldArg) arg;
|
||||||
if (f.isStatic()) {
|
if (f.isStatic()) {
|
||||||
@@ -128,7 +132,14 @@ public class InsnGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
|
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) {
|
if (fieldNode != null) {
|
||||||
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
||||||
if (replace != null) {
|
if (replace != null) {
|
||||||
@@ -178,7 +189,7 @@ public class InsnGen {
|
|||||||
mgen.getClassGen().useClass(code, cls);
|
mgen.getClassGen().useClass(code, cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void useType(CodeWriter code, ArgType type) {
|
protected void useType(CodeWriter code, ArgType type) {
|
||||||
mgen.getClassGen().useType(code, type);
|
mgen.getClassGen().useType(code, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,26 +197,27 @@ public class InsnGen {
|
|||||||
return makeInsn(insn, code, null);
|
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 {
|
try {
|
||||||
if (insn.getType() == InsnType.NOP) {
|
if (insn.getType() == InsnType.NOP) {
|
||||||
return false;
|
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) {
|
if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {
|
||||||
state.add(flag);
|
state.add(flag);
|
||||||
makeInsnBody(code, insn, state);
|
makeInsnBody(code, insn, state);
|
||||||
} else {
|
} else {
|
||||||
code.startLine();
|
if (flag != Flags.INLINE) {
|
||||||
if (insn.getSourceLine() != 0) {
|
code.startLineWithNum(insn.getSourceLine());
|
||||||
code.attachSourceLine(insn.getSourceLine());
|
|
||||||
}
|
}
|
||||||
if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) {
|
if (insn.getResult() != null && !insn.contains(AFlag.ARITH_ONEARG)) {
|
||||||
assignVar(code, insn);
|
assignVar(code, insn);
|
||||||
code.add(" = ");
|
code.add(" = ");
|
||||||
}
|
}
|
||||||
makeInsnBody(code, insn, state);
|
makeInsnBody(code, insn, state);
|
||||||
code.add(';');
|
if (flag != Flags.INLINE) {
|
||||||
|
code.add(';');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
throw new CodegenException(mth, "Error generate insn: " + insn, th);
|
throw new CodegenException(mth, "Error generate insn: " + insn, th);
|
||||||
@@ -213,7 +225,7 @@ public class InsnGen {
|
|||||||
return true;
|
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()) {
|
switch (insn.getType()) {
|
||||||
case CONST_STR:
|
case CONST_STR:
|
||||||
String str = ((ConstStringNode) insn).getString();
|
String str = ((ConstStringNode) insn).getString();
|
||||||
@@ -255,10 +267,6 @@ public class InsnGen {
|
|||||||
makeArith((ArithNode) insn, code, state);
|
makeArith((ArithNode) insn, code, state);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARITH_ONEARG:
|
|
||||||
makeArithOneArg((ArithNode) insn, code);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEG: {
|
case NEG: {
|
||||||
boolean wrap = state.contains(Flags.BODY_ONLY);
|
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
@@ -283,6 +291,10 @@ public class InsnGen {
|
|||||||
|
|
||||||
case BREAK:
|
case BREAK:
|
||||||
code.add("break");
|
code.add("break");
|
||||||
|
LoopLabelAttr labelAttr = insn.get(AType.LOOP_LABEL);
|
||||||
|
if (labelAttr != null) {
|
||||||
|
code.add(' ').add(mgen.getNameGen().getLoopLabel(labelAttr));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTINUE:
|
case CONTINUE:
|
||||||
@@ -304,7 +316,7 @@ public class InsnGen {
|
|||||||
addArg(code, insn.getArg(0));
|
addArg(code, insn.getArg(0));
|
||||||
code.add(" == ");
|
code.add(" == ");
|
||||||
addArg(code, insn.getArg(1));
|
addArg(code, insn.getArg(1));
|
||||||
code.add("? 0 : -1))");
|
code.add(" ? 0 : -1))");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSTANCE_OF: {
|
case INSTANCE_OF: {
|
||||||
@@ -425,19 +437,11 @@ public class InsnGen {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOVE_EXCEPTION:
|
|
||||||
if (isFallback()) {
|
|
||||||
code.add("move-exception");
|
|
||||||
} else {
|
|
||||||
addArg(code, insn.getArg(0));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TERNARY:
|
case TERNARY:
|
||||||
makeTernary((TernaryInsn) insn, code, state);
|
makeTernary((TernaryInsn) insn, code, state);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARGS:
|
case ONE_ARG:
|
||||||
addArg(code, insn.getArg(0));
|
addArg(code, insn.getArg(0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -461,6 +465,11 @@ public class InsnGen {
|
|||||||
code.add("goto ").add(MethodGen.getLabelName(((GotoNode) insn).getTarget()));
|
code.add("goto ").add(MethodGen.getLabelName(((GotoNode) insn).getTarget()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MOVE_EXCEPTION:
|
||||||
|
assert isFallback();
|
||||||
|
code.add("move-exception");
|
||||||
|
break;
|
||||||
|
|
||||||
case SWITCH:
|
case SWITCH:
|
||||||
assert isFallback();
|
assert isFallback();
|
||||||
SwitchNode sw = (SwitchNode) insn;
|
SwitchNode sw = (SwitchNode) insn;
|
||||||
@@ -505,6 +514,13 @@ public class InsnGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fillArray(FillArrayNode insn, CodeWriter code) throws CodegenException {
|
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 insnArrayType = insn.getResult().getType();
|
||||||
ArgType insnElementType = insnArrayType.getArrayElement();
|
ArgType insnElementType = insnArrayType.getArrayElement();
|
||||||
ArgType elType = insn.getElementType();
|
ArgType elType = insn.getElementType();
|
||||||
@@ -513,10 +529,13 @@ public class InsnGen {
|
|||||||
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
||||||
+ ", element type: " + elType + ", insn element type: " + insnElementType
|
+ ", 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();
|
StringBuilder str = new StringBuilder();
|
||||||
Object data = insn.getData();
|
Object data = insn.getData();
|
||||||
switch (elType.getPrimitiveType()) {
|
switch (elType.getPrimitiveType()) {
|
||||||
@@ -558,9 +577,7 @@ public class InsnGen {
|
|||||||
}
|
}
|
||||||
int len = str.length();
|
int len = str.length();
|
||||||
str.delete(len - 2, len);
|
str.delete(len - 2, len);
|
||||||
code.add("new ");
|
return str.toString();
|
||||||
useType(code, elType);
|
|
||||||
code.add("[]{").add(str.toString()).add('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
||||||
@@ -604,7 +621,7 @@ public class InsnGen {
|
|||||||
code.add("new ");
|
code.add("new ");
|
||||||
useClass(code, insn.getClassType());
|
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 {
|
private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||||
@@ -649,47 +666,74 @@ public class InsnGen {
|
|||||||
code.attachAnnotation(callMthNode);
|
code.attachAnnotation(callMthNode);
|
||||||
}
|
}
|
||||||
code.add(callMth.getName());
|
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)) {
|
if (callMth != null && callMth.contains(AFlag.SKIP_FIRST_ARG)) {
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
int argsCount = insn.getArgsCount();
|
int argsCount = insn.getArgsCount();
|
||||||
if (callMth != null && callMth.isArgsOverload()) {
|
code.add('(');
|
||||||
// add additional argument casts for overloaded methods
|
if (k < argsCount) {
|
||||||
List<ArgType> originalType = callMth.getMethodInfo().getArgumentsTypes();
|
boolean overloaded = callMth != null && callMth.isArgsOverload();
|
||||||
int origPos = 0;
|
|
||||||
code.add('(');
|
|
||||||
for (int i = k; i < argsCount; i++) {
|
for (int i = k; i < argsCount; i++) {
|
||||||
InsnArg arg = insn.getArg(i);
|
InsnArg arg = insn.getArg(i);
|
||||||
ArgType origType = originalType.get(origPos);
|
boolean cast = overloaded && processOverloadedArg(code, callMth, arg, i - startArgNum);
|
||||||
if (!arg.getType().equals(origType)) {
|
if (!cast && i == argsCount - 1 && processVarArg(code, callMth, arg)) {
|
||||||
code.add('(');
|
continue;
|
||||||
useType(code, origType);
|
|
||||||
code.add(')');
|
|
||||||
addArg(code, arg, true);
|
|
||||||
} else {
|
|
||||||
addArg(code, arg, false);
|
|
||||||
}
|
}
|
||||||
|
addArg(code, arg, false);
|
||||||
if (i < argsCount - 1) {
|
if (i < argsCount - 1) {
|
||||||
code.add(", ");
|
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('(');
|
code.add('(');
|
||||||
if (k < argsCount) {
|
useType(code, origType);
|
||||||
addArg(code, insn.getArg(k), false);
|
code.add(") ");
|
||||||
for (int i = k + 1; i < argsCount; i++) {
|
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(", ");
|
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 {
|
private boolean inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||||
@@ -736,7 +780,7 @@ public class InsnGen {
|
|||||||
return true;
|
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);
|
boolean wrap = state.contains(Flags.BODY_ONLY);
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
code.add('(');
|
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
|
// wrap insn in brackets for save correct operation order
|
||||||
boolean wrap = state.contains(Flags.BODY_ONLY) && !insn.contains(AFlag.DONT_WRAP);
|
boolean wrap = state.contains(Flags.BODY_ONLY) && !insn.contains(AFlag.DONT_WRAP);
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
@@ -776,7 +824,7 @@ public class InsnGen {
|
|||||||
|
|
||||||
private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException {
|
private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException {
|
||||||
ArithOp op = insn.getOp();
|
ArithOp op = insn.getOp();
|
||||||
InsnArg arg = insn.getArg(0);
|
InsnArg arg = insn.getArg(1);
|
||||||
// "++" or "--"
|
// "++" or "--"
|
||||||
if (arg.isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {
|
if (arg.isLiteral() && (op == ArithOp.ADD || op == ArithOp.SUB)) {
|
||||||
LiteralArg lit = (LiteralArg) arg;
|
LiteralArg lit = (LiteralArg) arg;
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ public class MethodGen {
|
|||||||
if (clsAccFlags.isAnnotation()) {
|
if (clsAccFlags.isAnnotation()) {
|
||||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||||
}
|
}
|
||||||
code.startLine(ai.makeString());
|
code.startLineWithNum(mth.getSourceLine());
|
||||||
code.attachSourceLine(mth.getSourceLine());
|
code.add(ai.makeString());
|
||||||
|
|
||||||
if (classGen.addGenericMap(code, mth.getGenericMap())) {
|
if (classGen.addGenericMap(code, mth.getGenericMap())) {
|
||||||
code.add(' ');
|
code.add(' ');
|
||||||
@@ -206,13 +206,10 @@ public class MethodGen {
|
|||||||
if (insn == null) {
|
if (insn == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (addLabels) {
|
if (addLabels && (insn.contains(AType.JUMP) || insn.contains(AType.EXC_HANDLER))) {
|
||||||
if (insn.contains(AType.JUMP)
|
code.decIndent();
|
||||||
|| insn.contains(AType.EXC_HANDLER)) {
|
code.startLine(getLabelName(insn.getOffset()) + ":");
|
||||||
code.decIndent();
|
code.incIndent();
|
||||||
code.startLine(getLabelName(insn.getOffset()) + ":");
|
|
||||||
code.incIndent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (insnGen.makeInsn(insn, code)) {
|
if (insnGen.makeInsn(insn, code)) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package jadx.core.codegen;
|
|||||||
|
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.deobf.NameMapper;
|
import jadx.core.deobf.NameMapper;
|
||||||
|
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.instructions.InvokeNode;
|
import jadx.core.dex.instructions.InvokeNode;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
@@ -53,10 +54,7 @@ public class NameGen {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
name = getUniqueVarName(name);
|
name = getUniqueVarName(name);
|
||||||
SSAVar sVar = arg.getSVar();
|
arg.setName(name);
|
||||||
if (sVar != null) {
|
|
||||||
sVar.setName(name);
|
|
||||||
}
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +69,16 @@ public class NameGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String useArg(RegisterArg arg) {
|
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);
|
varNames.add(name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -88,14 +95,10 @@ public class NameGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String makeArgName(RegisterArg arg) {
|
private String makeArgName(RegisterArg arg) {
|
||||||
String name = arg.getName();
|
|
||||||
if (fallback) {
|
if (fallback) {
|
||||||
String base = "r" + arg.getRegNum();
|
return getFallbackName(arg);
|
||||||
if (name != null && !name.equals("this")) {
|
|
||||||
return base + "_" + name;
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
}
|
}
|
||||||
|
String name = arg.getName();
|
||||||
String varName;
|
String varName;
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
if ("this".equals(name)) {
|
if ("this".equals(name)) {
|
||||||
@@ -111,6 +114,15 @@ public class NameGen {
|
|||||||
return varName;
|
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) {
|
private static String makeNameForType(ArgType type) {
|
||||||
if (type.isPrimitive()) {
|
if (type.isPrimitive()) {
|
||||||
return makeNameForPrimitive(type);
|
return makeNameForPrimitive(type);
|
||||||
|
|||||||
@@ -4,27 +4,35 @@ import jadx.core.dex.attributes.AFlag;
|
|||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||||
|
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.instructions.IndexInsnNode;
|
import jadx.core.dex.instructions.IndexInsnNode;
|
||||||
import jadx.core.dex.instructions.SwitchNode;
|
import jadx.core.dex.instructions.SwitchNode;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.IBlock;
|
import jadx.core.dex.nodes.IBlock;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
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.Region;
|
||||||
import jadx.core.dex.regions.SwitchRegion;
|
import jadx.core.dex.regions.SwitchRegion;
|
||||||
import jadx.core.dex.regions.SynchronizedRegion;
|
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.ExceptionHandler;
|
||||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||||
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.RegionUtils;
|
import jadx.core.utils.RegionUtils;
|
||||||
import jadx.core.utils.exceptions.CodegenException;
|
import jadx.core.utils.exceptions.CodegenException;
|
||||||
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -52,6 +60,8 @@ public class RegionGen extends InsnGen {
|
|||||||
makeSwitch((SwitchRegion) cont, code);
|
makeSwitch((SwitchRegion) cont, code);
|
||||||
} else if (cont instanceof LoopRegion) {
|
} else if (cont instanceof LoopRegion) {
|
||||||
makeLoop((LoopRegion) cont, code);
|
makeLoop((LoopRegion) cont, code);
|
||||||
|
} else if (cont instanceof TryCatchRegion) {
|
||||||
|
makeTryCatch((TryCatchRegion) cont, code);
|
||||||
} else if (cont instanceof SynchronizedRegion) {
|
} else if (cont instanceof SynchronizedRegion) {
|
||||||
makeSynchronizedRegion((SynchronizedRegion) cont, code);
|
makeSynchronizedRegion((SynchronizedRegion) cont, code);
|
||||||
}
|
}
|
||||||
@@ -73,14 +83,9 @@ public class RegionGen extends InsnGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void makeSimpleRegion(CodeWriter code, Region region) throws CodegenException {
|
private void makeSimpleRegion(CodeWriter code, Region region) throws CodegenException {
|
||||||
CatchAttr tc = region.get(AType.CATCH_BLOCK);
|
declareVars(code, region);
|
||||||
if (tc != null) {
|
for (IContainer c : region.getSubBlocks()) {
|
||||||
makeTryCatch(region, tc.getTryBlock(), code);
|
makeRegion(code, c);
|
||||||
} else {
|
|
||||||
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 {
|
private void makeSimpleBlock(IBlock block, CodeWriter code) throws CodegenException {
|
||||||
for (InsnNode insn : block.getInstructions()) {
|
for (InsnNode insn : block.getInstructions()) {
|
||||||
makeInsn(insn, code);
|
if (!insn.contains(AFlag.SKIP)) {
|
||||||
|
makeInsn(insn, code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ForceReturnAttr retAttr = block.get(AType.FORCE_RETURN);
|
ForceReturnAttr retAttr = block.get(AType.FORCE_RETURN);
|
||||||
if (retAttr != null) {
|
if (retAttr != null) {
|
||||||
@@ -101,14 +108,11 @@ public class RegionGen extends InsnGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void makeIf(IfRegion region, CodeWriter code, boolean newLine) throws CodegenException {
|
private void makeIf(IfRegion region, CodeWriter code, boolean newLine) throws CodegenException {
|
||||||
if (region.getTernRegion() != null) {
|
|
||||||
makeSimpleBlock(region.getTernRegion().getBlock(), code);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (newLine) {
|
if (newLine) {
|
||||||
code.startLine();
|
code.startLineWithNum(region.getSourceLine());
|
||||||
|
} else {
|
||||||
|
code.attachSourceLine(region.getSourceLine());
|
||||||
}
|
}
|
||||||
code.attachSourceLine(region.getSourceLine());
|
|
||||||
code.add("if (");
|
code.add("if (");
|
||||||
new ConditionGen(this).add(code, region.getCondition());
|
new ConditionGen(this).add(code, region.getCondition());
|
||||||
code.add(") {");
|
code.add(") {");
|
||||||
@@ -151,8 +155,7 @@ public class RegionGen extends InsnGen {
|
|||||||
if (header != null) {
|
if (header != null) {
|
||||||
List<InsnNode> headerInsns = header.getInstructions();
|
List<InsnNode> headerInsns = header.getInstructions();
|
||||||
if (headerInsns.size() > 1) {
|
if (headerInsns.size() > 1) {
|
||||||
// write not inlined instructions from header
|
ErrorsCounter.methodError(mth, "Found not inlined instructions from loop header");
|
||||||
mth.add(AFlag.INCONSISTENT_CODE);
|
|
||||||
int last = headerInsns.size() - 1;
|
int last = headerInsns.size() - 1;
|
||||||
for (int i = 0; i < last; i++) {
|
for (int i = 0; i < last; i++) {
|
||||||
InsnNode insn = headerInsns.get(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();
|
IfCondition condition = region.getCondition();
|
||||||
if (condition == null) {
|
if (condition == null) {
|
||||||
@@ -169,8 +176,35 @@ public class RegionGen extends InsnGen {
|
|||||||
code.startLine('}');
|
code.startLine('}');
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConditionGen conditionGen = new ConditionGen(this);
|
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()) {
|
if (region.isConditionAtEnd()) {
|
||||||
code.startLine("do {");
|
code.startLine("do {");
|
||||||
makeRegionIndent(code, region.getBody());
|
makeRegionIndent(code, region.getBody());
|
||||||
@@ -209,7 +243,14 @@ public class RegionGen extends InsnGen {
|
|||||||
IContainer c = sw.getCases().get(i);
|
IContainer c = sw.getCases().get(i);
|
||||||
for (Object k : keys) {
|
for (Object k : keys) {
|
||||||
code.startLine("case ");
|
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());
|
staticField(code, (FieldInfo) ((IndexInsnNode) k).getIndex());
|
||||||
} else {
|
} else {
|
||||||
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
|
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)
|
private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException {
|
||||||
throws CodegenException {
|
TryCatchBlock tryCatchBlock = region.geTryCatchBlock();
|
||||||
code.startLine("try {");
|
code.startLine("try {");
|
||||||
region.remove(AType.CATCH_BLOCK);
|
makeRegionIndent(code, region.getTryRegion());
|
||||||
makeRegionIndent(code, region);
|
// TODO: move search of 'allHandler' to 'TryCatchRegion'
|
||||||
ExceptionHandler allHandler = null;
|
ExceptionHandler allHandler = null;
|
||||||
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
||||||
if (!handler.isCatchAll()) {
|
if (!handler.isCatchAll()) {
|
||||||
makeCatchBlock(code, handler);
|
makeCatchBlock(code, handler);
|
||||||
} else {
|
} else {
|
||||||
if (allHandler != null) {
|
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;
|
allHandler = handler;
|
||||||
}
|
}
|
||||||
@@ -266,20 +307,25 @@ public class RegionGen extends InsnGen {
|
|||||||
code.startLine('}');
|
code.startLine('}');
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler)
|
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler) throws CodegenException {
|
||||||
throws CodegenException {
|
|
||||||
IContainer region = handler.getHandlerRegion();
|
IContainer region = handler.getHandlerRegion();
|
||||||
if (region != null) {
|
if (region == null) {
|
||||||
code.startLine("} catch (");
|
return;
|
||||||
|
}
|
||||||
|
code.startLine("} catch (");
|
||||||
|
InsnArg arg = handler.getArg();
|
||||||
|
if (arg instanceof RegisterArg) {
|
||||||
|
declareVar(code, (RegisterArg) arg);
|
||||||
|
} else if (arg instanceof NamedArg) {
|
||||||
if (handler.isCatchAll()) {
|
if (handler.isCatchAll()) {
|
||||||
code.add("Throwable");
|
code.add("Throwable");
|
||||||
} else {
|
} else {
|
||||||
useClass(code, handler.getCatchType());
|
useClass(code, handler.getCatchType());
|
||||||
}
|
}
|
||||||
code.add(' ');
|
code.add(' ');
|
||||||
code.add(mgen.getNameGen().assignNamedArg(handler.getArg()));
|
code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
|
||||||
code.add(") {");
|
|
||||||
makeRegionIndent(code, region);
|
|
||||||
}
|
}
|
||||||
|
code.add(") {");
|
||||||
|
makeRegionIndent(code, region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ import jadx.core.utils.StringUtils;
|
|||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class TypeGen {
|
public class TypeGen {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TypeGen.class);
|
||||||
|
|
||||||
|
private TypeGen() {
|
||||||
|
}
|
||||||
|
|
||||||
public static String signature(ArgType type) {
|
public static String signature(ArgType type) {
|
||||||
PrimitiveType stype = type.getPrimitiveType();
|
PrimitiveType stype = type.getPrimitiveType();
|
||||||
@@ -56,7 +63,8 @@ public class TypeGen {
|
|||||||
case OBJECT:
|
case OBJECT:
|
||||||
case ARRAY:
|
case ARRAY:
|
||||||
if (lit != 0) {
|
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";
|
return "null";
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public enum AFlag {
|
|||||||
DONT_WRAP,
|
DONT_WRAP,
|
||||||
|
|
||||||
DONT_SHRINK,
|
DONT_SHRINK,
|
||||||
|
DONT_INLINE,
|
||||||
DONT_GENERATE,
|
DONT_GENERATE,
|
||||||
SKIP,
|
SKIP,
|
||||||
|
|
||||||
@@ -23,5 +24,8 @@ public enum AFlag {
|
|||||||
|
|
||||||
ELSE_IF_CHAIN,
|
ELSE_IF_CHAIN,
|
||||||
|
|
||||||
|
WRAPPED,
|
||||||
|
ARITH_ONEARG,
|
||||||
|
|
||||||
INCONSISTENT_CODE, // warning about incorrect decompilation
|
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.annotations.MethodParameters;
|
||||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
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.FieldReplaceAttr;
|
||||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
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.MethodInlineAttr;
|
||||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||||
@@ -28,6 +30,8 @@ public class AType<T extends IAttribute> {
|
|||||||
private AType() {
|
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<JumpInfo>> JUMP = new AType<AttrList<JumpInfo>>();
|
||||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<AttrList<LoopInfo>>();
|
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<JadxErrorAttr> JADX_ERROR = new AType<JadxErrorAttr>();
|
||||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
|
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
|
||||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<EnumClassAttr>();
|
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<AnnotationsList> ANNOTATION_LIST = new AType<AnnotationsList>();
|
||||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<MethodParameters>();
|
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<PhiListAttr> PHI_LIST = new AType<PhiListAttr>();
|
||||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
|
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
|
||||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
|
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
|
@Override
|
||||||
public void add(AFlag flag) {
|
public void add(AFlag flag) {
|
||||||
getStorage().add(flag);
|
initStorage().add(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAttr(IAttribute attr) {
|
public void addAttr(IAttribute attr) {
|
||||||
getStorage().add(attr);
|
initStorage().add(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void addAttr(AType<AttrList<T>> type, T obj) {
|
public <T> void addAttr(AType<AttrList<T>> type, T obj) {
|
||||||
getStorage().add(type, obj);
|
initStorage().add(type, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyAttributesFrom(AttrNode attrNode) {
|
public void copyAttributesFrom(AttrNode attrNode) {
|
||||||
getStorage().addAll(attrNode.storage);
|
initStorage().addAll(attrNode.storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributeStorage getStorage() {
|
AttributeStorage initStorage() {
|
||||||
AttributeStorage store = storage;
|
AttributeStorage store = storage;
|
||||||
if (store == EMPTY_ATTR_STORAGE) {
|
if (store == EMPTY_ATTR_STORAGE) {
|
||||||
store = new AttributeStorage();
|
store = new AttributeStorage();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import jadx.core.utils.Utils;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -24,7 +24,7 @@ public class AttributeStorage {
|
|||||||
|
|
||||||
public AttributeStorage() {
|
public AttributeStorage() {
|
||||||
flags = EnumSet.noneOf(AFlag.class);
|
flags = EnumSet.noneOf(AFlag.class);
|
||||||
attributes = new HashMap<AType<?>, IAttribute>(2);
|
attributes = new IdentityHashMap<AType<?>, IAttribute>(AType.FIELDS_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(AFlag flag) {
|
public void add(AFlag flag) {
|
||||||
@@ -72,7 +72,7 @@ public class AttributeStorage {
|
|||||||
if (attrList == null) {
|
if (attrList == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return attrList.getList();
|
return Collections.unmodifiableList(attrList.getList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(AFlag flag) {
|
public void remove(AFlag flag) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import jadx.core.dex.attributes.annotations.Annotation;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class EmptyAttrStorage extends AttributeStorage {
|
public final class EmptyAttrStorage extends AttributeStorage {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(AFlag flag) {
|
public boolean contains(AFlag flag) {
|
||||||
@@ -52,4 +52,9 @@ public class EmptyAttrStorage extends AttributeStorage {
|
|||||||
public List<String> getAttributeStrings() {
|
public List<String> getAttributeStrings() {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ public class AnnotationsList implements IAttribute {
|
|||||||
return map.size();
|
return map.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<AnnotationsList> getType() {
|
public AType<AnnotationsList> getType() {
|
||||||
return AType.ANNOTATION_LIST;
|
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 BlockNode end;
|
||||||
private final Set<BlockNode> loopBlocks;
|
private final Set<BlockNode> loopBlocks;
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
private LoopInfo parentLoop;
|
||||||
|
|
||||||
public LoopInfo(BlockNode start, BlockNode end) {
|
public LoopInfo(BlockNode start, BlockNode end) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
@@ -69,8 +72,24 @@ public class LoopInfo {
|
|||||||
return edges;
|
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
|
@Override
|
||||||
public String toString() {
|
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;
|
package jadx.core.dex.instructions;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
@@ -51,10 +52,8 @@ public class ArithNode extends InsnNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ArithNode(ArithOp op, RegisterArg res, InsnArg a) {
|
public ArithNode(ArithOp op, RegisterArg res, InsnArg a) {
|
||||||
super(InsnType.ARITH_ONEARG, 1);
|
this(op, res, res, a);
|
||||||
this.op = op;
|
add(AFlag.ARITH_ONEARG);
|
||||||
setResult(res);
|
|
||||||
addArg(a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArithOp getOp() {
|
public ArithOp getOp() {
|
||||||
@@ -85,7 +84,7 @@ public class ArithNode extends InsnNode {
|
|||||||
+ getResult() + " = "
|
+ getResult() + " = "
|
||||||
+ getArg(0) + " "
|
+ getArg(0) + " "
|
||||||
+ op.getSymbol() + " "
|
+ 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.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.PrimitiveType;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.DexNode;
|
import jadx.core.dex.nodes.DexNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.utils.InsnUtils;
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
import jadx.core.utils.exceptions.DecodeException;
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
|
|
||||||
import com.android.dex.Code;
|
import com.android.dex.Code;
|
||||||
import com.android.dx.io.OpcodeInfo;
|
import com.android.dx.io.OpcodeInfo;
|
||||||
import com.android.dx.io.Opcodes;
|
import com.android.dx.io.Opcodes;
|
||||||
@@ -44,7 +42,7 @@ public class InsnDecoder {
|
|||||||
while (in.hasMore()) {
|
while (in.hasMore()) {
|
||||||
decoded[in.cursor()] = DecodedInstruction.decode(in);
|
decoded[in.cursor()] = DecodedInstruction.decode(in);
|
||||||
}
|
}
|
||||||
} catch (EOFException e) {
|
} catch (Exception e) {
|
||||||
throw new DecodeException(method, "", e);
|
throw new DecodeException(method, "", e);
|
||||||
}
|
}
|
||||||
insnArr = decoded;
|
insnArr = decoded;
|
||||||
@@ -406,8 +404,7 @@ public class InsnDecoder {
|
|||||||
|
|
||||||
case Opcodes.MOVE_EXCEPTION:
|
case Opcodes.MOVE_EXCEPTION:
|
||||||
return insn(InsnType.MOVE_EXCEPTION,
|
return insn(InsnType.MOVE_EXCEPTION,
|
||||||
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)),
|
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)));
|
||||||
new NamedArg("e", ArgType.unknown(PrimitiveType.OBJECT)));
|
|
||||||
|
|
||||||
case Opcodes.RETURN_VOID:
|
case Opcodes.RETURN_VOID:
|
||||||
return new InsnNode(InsnType.RETURN, 0);
|
return new InsnNode(InsnType.RETURN, 0);
|
||||||
@@ -624,16 +621,19 @@ public class InsnDecoder {
|
|||||||
int resReg = getMoveResultRegister(insnArr, offset);
|
int resReg = getMoveResultRegister(insnArr, offset);
|
||||||
ArgType arrType = dex.getType(insn.getIndex());
|
ArgType arrType = dex.getType(insn.getIndex());
|
||||||
ArgType elType = arrType.getArrayElement();
|
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) {
|
if (isRange) {
|
||||||
int r = insn.getA();
|
int r = insn.getA();
|
||||||
for (int i = 0; i < insn.getRegisterCount(); i++) {
|
for (int i = 0; i < regsCount; i++) {
|
||||||
regs[i] = InsnArg.reg(r, elType);
|
regs[i] = InsnArg.reg(r, elType, typeImmutable);
|
||||||
r++;
|
r++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < insn.getRegisterCount(); i++) {
|
for (int i = 0; i < regsCount; i++) {
|
||||||
regs[i] = InsnArg.reg(insn, i, elType);
|
int regNum = InsnUtils.getArg(insn, i);
|
||||||
|
regs[i] = InsnArg.reg(regNum, elType, typeImmutable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return insn(InsnType.FILLED_NEW_ARRAY,
|
return insn(InsnType.FILLED_NEW_ARRAY,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package jadx.core.dex.instructions;
|
package jadx.core.dex.instructions;
|
||||||
|
|
||||||
public enum InsnType {
|
public enum InsnType {
|
||||||
NOP, // replacement for removed instructions
|
|
||||||
|
|
||||||
CONST,
|
CONST,
|
||||||
CONST_STR,
|
CONST_STR,
|
||||||
@@ -48,17 +47,24 @@ public enum InsnType {
|
|||||||
|
|
||||||
INVOKE,
|
INVOKE,
|
||||||
|
|
||||||
// additional instructions
|
// *** Additional instructions ***
|
||||||
|
|
||||||
|
// replacement for removed instructions
|
||||||
|
NOP,
|
||||||
|
|
||||||
|
TERNARY,
|
||||||
CONSTRUCTOR,
|
CONSTRUCTOR,
|
||||||
|
|
||||||
BREAK,
|
BREAK,
|
||||||
CONTINUE,
|
CONTINUE,
|
||||||
|
|
||||||
STR_CONCAT, // strings concatenation
|
// strings concatenation
|
||||||
ARITH_ONEARG,
|
STR_CONCAT,
|
||||||
|
|
||||||
TERNARY,
|
// just generate one argument
|
||||||
ARGS, // just generate arguments
|
ONE_ARG,
|
||||||
PHI,
|
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;
|
package jadx.core.dex.instructions;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
@@ -14,6 +15,7 @@ public class PhiInsn extends InsnNode {
|
|||||||
for (int i = 0; i < predecessors; i++) {
|
for (int i = 0; i < predecessors; i++) {
|
||||||
addReg(regNum, ArgType.UNKNOWN);
|
addReg(regNum, ArgType.UNKNOWN);
|
||||||
}
|
}
|
||||||
|
add(AFlag.DONT_INLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -21,6 +23,14 @@ public class PhiInsn extends InsnNode {
|
|||||||
return (RegisterArg) super.getArg(n);
|
return (RegisterArg) super.getArg(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean removeArg(RegisterArg arg) {
|
||||||
|
boolean isRemoved = super.removeArg(arg);
|
||||||
|
if (isRemoved) {
|
||||||
|
arg.getSVar().setUsedInPhi(null);
|
||||||
|
}
|
||||||
|
return isRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments());
|
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments());
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public abstract class ArgType {
|
public abstract class ArgType {
|
||||||
|
|
||||||
public static final ArgType INT = primitive(PrimitiveType.INT);
|
public static final ArgType INT = primitive(PrimitiveType.INT);
|
||||||
@@ -93,10 +95,28 @@ public abstract class ArgType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class KnownType extends ArgType {
|
private abstract static class KnownType extends ArgType {
|
||||||
|
|
||||||
|
private static final PrimitiveType[] EMPTY_POSSIBLES = new PrimitiveType[0];
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTypeKnown() {
|
public boolean isTypeKnown() {
|
||||||
return true;
|
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 {
|
private static final class PrimitiveArg extends KnownType {
|
||||||
@@ -269,6 +289,7 @@ public abstract class ArgType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class ArrayArg extends KnownType {
|
private static final class ArrayArg extends KnownType {
|
||||||
|
public static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[]{PrimitiveType.ARRAY};
|
||||||
private final ArgType arrayElement;
|
private final ArgType arrayElement;
|
||||||
|
|
||||||
public ArrayArg(ArgType arrayElement) {
|
public ArrayArg(ArgType arrayElement) {
|
||||||
@@ -291,6 +312,21 @@ public abstract class ArgType {
|
|||||||
return PrimitiveType.ARRAY;
|
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
|
@Override
|
||||||
public int getArrayDimension() {
|
public int getArrayDimension() {
|
||||||
return 1 + arrayElement.getArrayDimension();
|
return 1 + arrayElement.getArrayDimension();
|
||||||
@@ -343,8 +379,10 @@ public abstract class ArgType {
|
|||||||
@Override
|
@Override
|
||||||
public ArgType selectFirst() {
|
public ArgType selectFirst() {
|
||||||
PrimitiveType f = possibleTypes[0];
|
PrimitiveType f = possibleTypes[0];
|
||||||
if (f == PrimitiveType.OBJECT || f == PrimitiveType.ARRAY) {
|
if (contains(PrimitiveType.OBJECT)) {
|
||||||
return object(Consts.CLASS_OBJECT);
|
return OBJECT;
|
||||||
|
} else if (contains(PrimitiveType.ARRAY)) {
|
||||||
|
return array(OBJECT);
|
||||||
} else {
|
} else {
|
||||||
return primitive(f);
|
return primitive(f);
|
||||||
}
|
}
|
||||||
@@ -428,18 +466,13 @@ public abstract class ArgType {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(PrimitiveType type) {
|
public abstract boolean contains(PrimitiveType type);
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArgType selectFirst() {
|
public abstract ArgType selectFirst();
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrimitiveType[] getPossibleTypes() {
|
public abstract PrimitiveType[] getPossibleTypes();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static ArgType merge(ArgType a, ArgType b) {
|
public static ArgType merge(ArgType a, ArgType b) {
|
||||||
if (a == null || b == null) {
|
if (a == null || b == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -458,13 +491,18 @@ public abstract class ArgType {
|
|||||||
if (a == UNKNOWN) {
|
if (a == UNKNOWN) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (a.isArray()) {
|
||||||
|
return mergeArrays((ArrayArg) a, b);
|
||||||
|
} else if (b.isArray()) {
|
||||||
|
return mergeArrays((ArrayArg) b, a);
|
||||||
|
}
|
||||||
if (!a.isTypeKnown()) {
|
if (!a.isTypeKnown()) {
|
||||||
if (b.isTypeKnown()) {
|
if (b.isTypeKnown()) {
|
||||||
if (a.contains(b.getPrimitiveType())) {
|
if (a.contains(b.getPrimitiveType())) {
|
||||||
return b;
|
return b;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
// both types unknown
|
// both types unknown
|
||||||
List<PrimitiveType> types = new ArrayList<PrimitiveType>();
|
List<PrimitiveType> types = new ArrayList<PrimitiveType>();
|
||||||
@@ -475,7 +513,8 @@ public abstract class ArgType {
|
|||||||
}
|
}
|
||||||
if (types.isEmpty()) {
|
if (types.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
} else if (types.size() == 1) {
|
}
|
||||||
|
if (types.size() == 1) {
|
||||||
PrimitiveType nt = types.get(0);
|
PrimitiveType nt = types.get(0);
|
||||||
if (nt == PrimitiveType.OBJECT || nt == PrimitiveType.ARRAY) {
|
if (nt == PrimitiveType.OBJECT || nt == PrimitiveType.ARRAY) {
|
||||||
return unknown(nt);
|
return unknown(nt);
|
||||||
@@ -499,31 +538,15 @@ public abstract class ArgType {
|
|||||||
String bObj = b.getObject();
|
String bObj = b.getObject();
|
||||||
if (aObj.equals(bObj)) {
|
if (aObj.equals(bObj)) {
|
||||||
return a.getGenericTypes() != null ? a : b;
|
return a.getGenericTypes() != null ? a : b;
|
||||||
} else if (aObj.equals(Consts.CLASS_OBJECT)) {
|
}
|
||||||
|
if (aObj.equals(Consts.CLASS_OBJECT)) {
|
||||||
return b;
|
return b;
|
||||||
} else if (bObj.equals(Consts.CLASS_OBJECT)) {
|
}
|
||||||
|
if (bObj.equals(Consts.CLASS_OBJECT)) {
|
||||||
return a;
|
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()) {
|
if (a.isPrimitive() && b.isPrimitive() && a.getRegCount() == b.getRegCount()) {
|
||||||
return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType()));
|
return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType()));
|
||||||
@@ -532,6 +555,25 @@ public abstract class ArgType {
|
|||||||
return null;
|
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) {
|
public static boolean isCastNeeded(ArgType from, ArgType to) {
|
||||||
if (from.equals(to)) {
|
if (from.equals(to)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -543,6 +585,16 @@ public abstract class ArgType {
|
|||||||
return true;
|
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) {
|
public static ArgType parse(String type) {
|
||||||
char f = type.charAt(0);
|
char f = type.charAt(0);
|
||||||
switch (f) {
|
switch (f) {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package jadx.core.dex.instructions.args;
|
package jadx.core.dex.instructions.args;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.utils.InsnUtils;
|
import jadx.core.utils.InsnUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -26,8 +30,12 @@ public abstract class InsnArg extends Typed {
|
|||||||
return reg(InsnUtils.getArg(insn, argNum), type);
|
return reg(InsnUtils.getArg(insn, argNum), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MthParameterArg parameterReg(int regNum, ArgType type) {
|
public static TypeImmutableArg typeImmutableReg(int regNum, ArgType type) {
|
||||||
return new MthParameterArg(regNum, 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) {
|
public static LiteralArg lit(long literal, ArgType type) {
|
||||||
@@ -76,18 +84,36 @@ public abstract class InsnArg extends Typed {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (parent == insn) {
|
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;
|
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();
|
int count = parent.getArgsCount();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (parent.getArg(i) == this) {
|
if (parent.getArg(i) == arg) {
|
||||||
InsnArg arg = wrapArg(insn);
|
return i;
|
||||||
parent.setArg(i, arg);
|
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InsnArg wrapArg(InsnNode insn) {
|
public static InsnArg wrapArg(InsnNode insn) {
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ public final class LiteralArg extends InsnArg {
|
|||||||
|
|
||||||
public boolean isInteger() {
|
public boolean isInteger() {
|
||||||
PrimitiveType type = this.type.getPrimitiveType();
|
PrimitiveType type = this.type.getPrimitiveType();
|
||||||
return (type == PrimitiveType.INT
|
return type == PrimitiveType.INT
|
||||||
|| type == PrimitiveType.BYTE
|
|| type == PrimitiveType.BYTE
|
||||||
|| type == PrimitiveType.CHAR
|
|| type == PrimitiveType.CHAR
|
||||||
|| type == PrimitiveType.SHORT
|
|| type == PrimitiveType.SHORT
|
||||||
|| type == PrimitiveType.LONG);
|
|| type == PrimitiveType.LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ public class RegisterArg extends InsnArg implements Named {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
|
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
|
||||||
|
|
||||||
protected final int regNum;
|
protected final int regNum;
|
||||||
protected SSAVar sVar;
|
// not null after SSATransform pass
|
||||||
|
private SSAVar sVar;
|
||||||
|
|
||||||
public RegisterArg(int rn) {
|
public RegisterArg(int rn) {
|
||||||
this.regNum = rn;
|
this.regNum = rn;
|
||||||
@@ -80,8 +81,10 @@ public class RegisterArg extends InsnArg implements Named {
|
|||||||
setName(name);
|
setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forceType(ArgType type) {
|
public RegisterArg duplicate() {
|
||||||
this.type = type;
|
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) {
|
if (sVar == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
RegisterArg assign = sVar.getAssign();
|
return sVar.getAssign().getParentInsn();
|
||||||
if (assign != null) {
|
|
||||||
return assign.getParentInsn();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InsnNode getPhiAssignInsn() {
|
public InsnNode getPhiAssignInsn() {
|
||||||
@@ -150,12 +149,9 @@ public class RegisterArg extends InsnArg implements Named {
|
|||||||
if (usePhi != null) {
|
if (usePhi != null) {
|
||||||
return usePhi;
|
return usePhi;
|
||||||
}
|
}
|
||||||
RegisterArg assign = sVar.getAssign();
|
InsnNode parent = sVar.getAssign().getParentInsn();
|
||||||
if (assign != null) {
|
if (parent != null && parent.getType() == InsnType.PHI) {
|
||||||
InsnNode parent = assign.getParentInsn();
|
return parent;
|
||||||
if (parent != null && parent.getType() == InsnType.PHI) {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,25 +5,74 @@ import jadx.core.dex.instructions.PhiInsn;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class SSAVar {
|
public class SSAVar {
|
||||||
|
|
||||||
private final int regNum;
|
private final int regNum;
|
||||||
private final int version;
|
private final int version;
|
||||||
private VarName varName;
|
private VarName varName;
|
||||||
|
|
||||||
|
private int startUseAddr;
|
||||||
|
private int endUseAddr;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
private RegisterArg assign;
|
private RegisterArg assign;
|
||||||
private final List<RegisterArg> useList = new ArrayList<RegisterArg>(2);
|
private final List<RegisterArg> useList = new ArrayList<RegisterArg>(2);
|
||||||
|
@Nullable
|
||||||
private PhiInsn usedInPhi;
|
private PhiInsn usedInPhi;
|
||||||
|
|
||||||
private ArgType type;
|
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.regNum = regNum;
|
||||||
this.version = v;
|
this.version = v;
|
||||||
this.assign = assign;
|
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;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
public RegisterArg getAssign() {
|
public RegisterArg getAssign() {
|
||||||
return assign;
|
return assign;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAssign(RegisterArg assign) {
|
public void setAssign(@NotNull RegisterArg assign) {
|
||||||
this.assign = 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;
|
this.usedInPhi = usedInPhi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public PhiInsn getUsedInPhi() {
|
public PhiInsn getUsedInPhi() {
|
||||||
return usedInPhi;
|
return usedInPhi;
|
||||||
}
|
}
|
||||||
@@ -81,24 +132,34 @@ public class SSAVar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getVariableUseCount() {
|
public int getVariableUseCount() {
|
||||||
if (!isUsedInPhi()) {
|
if (usedInPhi == null) {
|
||||||
return useList.size();
|
return useList.size();
|
||||||
}
|
}
|
||||||
return useList.size() + usedInPhi.getResult().getSVar().getUseCount();
|
return useList.size() + usedInPhi.getResult().getSVar().getUseCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgType getType() {
|
public void setType(ArgType type) {
|
||||||
return 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) {
|
public void setTypeImmutable(ArgType type) {
|
||||||
this.type = type;
|
setType(type);
|
||||||
if (assign != null) {
|
this.typeImmutable = true;
|
||||||
assign.type = type;
|
}
|
||||||
}
|
|
||||||
for (int i = 0, useListSize = useList.size(); i < useListSize; i++) {
|
public boolean isTypeImmutable() {
|
||||||
useList.get(i).type = type;
|
return typeImmutable;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
|
|||||||
+6
-4
@@ -1,10 +1,10 @@
|
|||||||
package jadx.core.dex.instructions.args;
|
package jadx.core.dex.instructions.args;
|
||||||
|
|
||||||
public class MthParameterArg extends RegisterArg {
|
public class TypeImmutableArg extends RegisterArg {
|
||||||
|
|
||||||
private boolean isThis;
|
private boolean isThis;
|
||||||
|
|
||||||
public MthParameterArg(int rn, ArgType type) {
|
public TypeImmutableArg(int rn, ArgType type) {
|
||||||
super(rn, type);
|
super(rn, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@ public class MthParameterArg extends RegisterArg {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setType(ArgType type) {
|
public void setType(ArgType type) {
|
||||||
|
// not allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsThis() {
|
public void markAsThis() {
|
||||||
@@ -39,6 +40,7 @@ public class MthParameterArg extends RegisterArg {
|
|||||||
if (isThis) {
|
if (isThis) {
|
||||||
sVar.setName("this");
|
sVar.setName("this");
|
||||||
}
|
}
|
||||||
|
sVar.setTypeImmutable(type);
|
||||||
super.setSVar(sVar);
|
super.setSVar(sVar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,13 +49,13 @@ public class MthParameterArg extends RegisterArg {
|
|||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(obj instanceof MthParameterArg)) {
|
if (!(obj instanceof TypeImmutableArg)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!super.equals(obj)) {
|
if (!super.equals(obj)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MthParameterArg that = (MthParameterArg) obj;
|
TypeImmutableArg that = (TypeImmutableArg) obj;
|
||||||
return isThis == that.isThis;
|
return isThis == that.isThis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +52,13 @@ public class ConstructorInsn extends InsnNode {
|
|||||||
setSourceLine(invoke.getSourceLine());
|
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() {
|
public MethodInfo getCallMth() {
|
||||||
return callMth;
|
return callMth;
|
||||||
}
|
}
|
||||||
@@ -64,6 +71,14 @@ public class ConstructorInsn extends InsnNode {
|
|||||||
return callMth.getDeclClass();
|
return callMth.getDeclClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CallType getCallType() {
|
||||||
|
return callType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNewInstance() {
|
||||||
|
return callType == CallType.CONSTRUCTOR;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSuper() {
|
public boolean isSuper() {
|
||||||
return callType == CallType.SUPER;
|
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.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
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.InsnUtils;
|
||||||
import jadx.core.utils.Utils;
|
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) {
|
public TernaryInsn(IfCondition condition, RegisterArg result, InsnArg th, InsnArg els) {
|
||||||
super(InsnType.TERNARY, 2);
|
super(InsnType.TERNARY, 2);
|
||||||
@@ -33,6 +39,26 @@ public class TernaryInsn extends InsnNode {
|
|||||||
return condition;
|
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
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
|||||||
try {
|
try {
|
||||||
new AnnotationsParser(this).parse(offset);
|
new AnnotationsParser(this).parse(offset);
|
||||||
} catch (DecodeException e) {
|
} 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) {
|
} 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);
|
field.setType(gType);
|
||||||
}
|
}
|
||||||
} catch (JadxRuntimeException e) {
|
} 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;
|
return parentClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassNode getTopParentClass() {
|
||||||
|
ClassNode parent = getParentClass();
|
||||||
|
return parent == this ? this : parent.getParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
public List<ClassNode> getInnerClasses() {
|
public List<ClassNode> getInnerClasses() {
|
||||||
return innerClasses;
|
return innerClasses;
|
||||||
}
|
}
|
||||||
@@ -377,10 +382,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
|||||||
|
|
||||||
public MethodNode getDefaultConstructor() {
|
public MethodNode getDefaultConstructor() {
|
||||||
for (MethodNode mth : methods) {
|
for (MethodNode mth : methods) {
|
||||||
if (mth.getAccessFlags().isConstructor()
|
if (mth.isDefaultConstructor()) {
|
||||||
&& mth.getMethodInfo().isConstructor()
|
|
||||||
&& (mth.getMethodInfo().getArgsCount() == 0
|
|
||||||
|| (mth.getArguments(false) != null && mth.getArguments(false).isEmpty()))) {
|
|
||||||
return mth;
|
return mth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.android.dex.ClassData;
|
import com.android.dex.ClassData;
|
||||||
import com.android.dex.ClassData.Method;
|
import com.android.dex.ClassData.Method;
|
||||||
import com.android.dex.ClassDef;
|
import com.android.dex.ClassDef;
|
||||||
@@ -31,16 +33,12 @@ public class DexNode {
|
|||||||
private final RootNode root;
|
private final RootNode root;
|
||||||
private final Dex dexBuf;
|
private final Dex dexBuf;
|
||||||
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||||
private final String[] strings;
|
|
||||||
|
|
||||||
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
|
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
|
||||||
|
|
||||||
public DexNode(RootNode root, InputFile input) {
|
public DexNode(RootNode root, InputFile input) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.dexBuf = input.getDexBuffer();
|
this.dexBuf = input.getDexBuffer();
|
||||||
|
|
||||||
List<String> stringList = dexBuf.strings();
|
|
||||||
this.strings = stringList.toArray(new String[stringList.size()]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadClasses() throws DecodeException {
|
public void loadClasses() throws DecodeException {
|
||||||
@@ -53,10 +51,12 @@ public class DexNode {
|
|||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public ClassNode resolveClass(ClassInfo clsInfo) {
|
public ClassNode resolveClass(ClassInfo clsInfo) {
|
||||||
return root.resolveClass(clsInfo);
|
return root.resolveClass(clsInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public MethodNode resolveMethod(MethodInfo mth) {
|
public MethodNode resolveMethod(MethodInfo mth) {
|
||||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
@@ -65,6 +65,7 @@ public class DexNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public FieldNode resolveField(FieldInfo field) {
|
public FieldNode resolveField(FieldInfo field) {
|
||||||
ClassNode cls = resolveClass(field.getDeclClass());
|
ClassNode cls = resolveClass(field.getDeclClass());
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
@@ -80,7 +81,7 @@ public class DexNode {
|
|||||||
// DexBuffer wrappers
|
// DexBuffer wrappers
|
||||||
|
|
||||||
public String getString(int index) {
|
public String getString(int index) {
|
||||||
return strings[index];
|
return dexBuf.strings().get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgType getType(int index) {
|
public ArgType getType(int index) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import jadx.core.dex.attributes.IAttributeNode;
|
|||||||
|
|
||||||
public interface IContainer extends IAttributeNode {
|
public interface IContainer extends IAttributeNode {
|
||||||
|
|
||||||
// unique id for use in 'toString()' method
|
/**
|
||||||
|
* Unique id for use in 'toString()' method
|
||||||
|
*/
|
||||||
String baseString();
|
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) {
|
public void setResult(RegisterArg res) {
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
res.setParentInsn(this);
|
res.setParentInsn(this);
|
||||||
@@ -101,6 +107,17 @@ public class InsnNode extends LineAttrNode {
|
|||||||
return false;
|
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) {
|
protected void addReg(DecodedInstruction insn, int i, ArgType type) {
|
||||||
addArg(InsnArg.reg(insn, i, 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() {
|
public boolean canReorder() {
|
||||||
switch (getType()) {
|
switch (getType()) {
|
||||||
case CONST:
|
case CONST:
|
||||||
@@ -153,7 +182,6 @@ public class InsnNode extends LineAttrNode {
|
|||||||
case NEW_ARRAY:
|
case NEW_ARRAY:
|
||||||
case NEW_MULTIDIM_ARRAY:
|
case NEW_MULTIDIM_ARRAY:
|
||||||
case STR_CONCAT:
|
case STR_CONCAT:
|
||||||
case MOVE_EXCEPTION:
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import jadx.core.dex.instructions.InsnDecoder;
|
|||||||
import jadx.core.dex.instructions.SwitchNode;
|
import jadx.core.dex.instructions.SwitchNode;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.RegisterArg;
|
||||||
import jadx.core.dex.instructions.args.SSAVar;
|
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.nodes.parser.SignatureParser;
|
||||||
import jadx.core.dex.regions.Region;
|
import jadx.core.dex.regions.Region;
|
||||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||||
@@ -34,6 +34,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
private final Method methodData;
|
private final Method methodData;
|
||||||
private int regsCount;
|
private int regsCount;
|
||||||
private InsnNode[] instructions;
|
private InsnNode[] instructions;
|
||||||
|
private int codeSize;
|
||||||
private int debugInfoOffset;
|
private int debugInfoOffset;
|
||||||
private boolean noCode;
|
private boolean noCode;
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
try {
|
try {
|
||||||
if (noCode) {
|
if (noCode) {
|
||||||
regsCount = 0;
|
regsCount = 0;
|
||||||
|
codeSize = 0;
|
||||||
initMethodTypes();
|
initMethodTypes();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -94,6 +97,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
InsnDecoder decoder = new InsnDecoder(this);
|
InsnDecoder decoder = new InsnDecoder(this);
|
||||||
decoder.decodeInsns(mthCode);
|
decoder.decodeInsns(mthCode);
|
||||||
instructions = decoder.process();
|
instructions = decoder.process();
|
||||||
|
codeSize = instructions.length;
|
||||||
|
|
||||||
initTryCatches(mthCode);
|
initTryCatches(mthCode);
|
||||||
initJumps();
|
initJumps();
|
||||||
@@ -144,8 +148,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!mthInfo.isConstructor()) {
|
if (!mthInfo.isConstructor()) {
|
||||||
LOG.warn("Wrong signature parse result: " + sp + " -> " + argsTypes
|
LOG.warn("Wrong signature parse result: {} -> {}, not generic version: {}", sp, argsTypes, mthArgs);
|
||||||
+ ", not generic version: " + mthArgs);
|
|
||||||
return false;
|
return false;
|
||||||
} else if (getParentClass().getAccessFlags().isEnum()) {
|
} else if (getParentClass().getAccessFlags().isEnum()) {
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -161,7 +164,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
}
|
}
|
||||||
initArguments(argsTypes);
|
initArguments(argsTypes);
|
||||||
} catch (JadxRuntimeException e) {
|
} catch (JadxRuntimeException e) {
|
||||||
LOG.error("Method signature parse error: " + this, e);
|
LOG.error("Method signature parse error: {}", this, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -180,7 +183,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
if (accFlags.isStatic()) {
|
if (accFlags.isStatic()) {
|
||||||
thisArg = null;
|
thisArg = null;
|
||||||
} else {
|
} else {
|
||||||
MthParameterArg arg = InsnArg.parameterReg(pos - 1, parentClass.getClassInfo().getType());
|
TypeImmutableArg arg = InsnArg.typeImmutableReg(pos - 1, parentClass.getClassInfo().getType());
|
||||||
arg.markAsThis();
|
arg.markAsThis();
|
||||||
thisArg = arg;
|
thisArg = arg;
|
||||||
}
|
}
|
||||||
@@ -190,7 +193,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
}
|
}
|
||||||
argsList = new ArrayList<RegisterArg>(args.size());
|
argsList = new ArrayList<RegisterArg>(args.size());
|
||||||
for (ArgType arg : args) {
|
for (ArgType arg : args) {
|
||||||
argsList.add(InsnArg.parameterReg(pos, arg));
|
argsList.add(InsnArg.typeImmutableReg(pos, arg));
|
||||||
pos += arg.getRegCount();
|
pos += arg.getRegCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,9 +204,8 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
list.add(thisArg);
|
list.add(thisArg);
|
||||||
list.addAll(argsList);
|
list.addAll(argsList);
|
||||||
return list;
|
return list;
|
||||||
} else {
|
|
||||||
return argsList;
|
|
||||||
}
|
}
|
||||||
|
return argsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegisterArg removeFirstArgument() {
|
public RegisterArg removeFirstArgument() {
|
||||||
@@ -227,6 +229,9 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
InsnNode[] insnByOffset = instructions;
|
InsnNode[] insnByOffset = instructions;
|
||||||
CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
|
CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
|
||||||
Try[] tries = mthCode.getTries();
|
Try[] tries = mthCode.getTries();
|
||||||
|
if (catchBlocks.length == 0 && tries.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int hc = 0;
|
int hc = 0;
|
||||||
Set<Integer> addrs = new HashSet<Integer>();
|
Set<Integer> addrs = new HashSet<Integer>();
|
||||||
@@ -271,7 +276,6 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
for (TryCatchBlock ct : catches) {
|
for (TryCatchBlock ct : catches) {
|
||||||
for (ExceptionHandler eh : ct.getHandlers()) {
|
for (ExceptionHandler eh : ct.getHandlers()) {
|
||||||
int addr = eh.getHandleOffset();
|
int addr = eh.getHandleOffset();
|
||||||
// assert addrs.add(addr) : "Instruction already contains EXC_HANDLER attribute";
|
|
||||||
ExcHandlerAttr ehAttr = new ExcHandlerAttr(ct, eh);
|
ExcHandlerAttr ehAttr = new ExcHandlerAttr(ct, eh);
|
||||||
insnByOffset[addr].addAttr(ehAttr);
|
insnByOffset[addr].addAttr(ehAttr);
|
||||||
}
|
}
|
||||||
@@ -284,13 +288,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
int offset = aTry.getStartAddress();
|
int offset = aTry.getStartAddress();
|
||||||
int end = offset + aTry.getInstructionCount() - 1;
|
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) {
|
while (offset <= end && offset >= 0) {
|
||||||
catchBlock.addInsn(insnByOffset[offset]);
|
insn = insnByOffset[offset];
|
||||||
|
catchBlock.addInsn(insn);
|
||||||
offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||||
}
|
}
|
||||||
if (insnByOffset[end] != null) {
|
if (insnByOffset[end] != null) {
|
||||||
insnByOffset[end].add(AFlag.TRY_LEAVE);
|
insnByOffset[end].add(AFlag.TRY_LEAVE);
|
||||||
|
} else {
|
||||||
|
insn.add(AFlag.TRY_LEAVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,18 +311,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case SWITCH: {
|
case SWITCH:
|
||||||
SwitchNode sw = (SwitchNode) insn;
|
SwitchNode sw = (SwitchNode) insn;
|
||||||
for (int target : sw.getTargets()) {
|
for (int target : sw.getTargets()) {
|
||||||
addJump(insnByOffset, offset, target);
|
addJump(insnByOffset, offset, target);
|
||||||
}
|
}
|
||||||
// default case
|
// default case
|
||||||
int next = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
int nextInsnOffset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||||
if (next != -1) {
|
if (nextInsnOffset != -1) {
|
||||||
addJump(insnByOffset, offset, next);
|
addJump(insnByOffset, offset, nextInsnOffset);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case IF:
|
case IF:
|
||||||
int next = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
int next = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
|
||||||
@@ -339,12 +346,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
String name = mthInfo.getName();
|
return mthInfo.getName();
|
||||||
if (name.equals(parentClass.getShortName())) {
|
|
||||||
return name + "_";
|
|
||||||
} else {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassNode getParentClass() {
|
public ClassNode getParentClass() {
|
||||||
@@ -355,6 +357,10 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
return noCode;
|
return noCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCodeSize() {
|
||||||
|
return codeSize;
|
||||||
|
}
|
||||||
|
|
||||||
public InsnNode[] getInstructions() {
|
public InsnNode[] getInstructions() {
|
||||||
return instructions;
|
return instructions;
|
||||||
}
|
}
|
||||||
@@ -404,10 +410,14 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
if (loops.isEmpty()) {
|
if (loops.isEmpty()) {
|
||||||
loops = new ArrayList<LoopInfo>(5);
|
loops = new ArrayList<LoopInfo>(5);
|
||||||
}
|
}
|
||||||
|
loop.setId(loops.size());
|
||||||
loops.add(loop);
|
loops.add(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoopInfo getLoopForBlock(BlockNode block) {
|
public LoopInfo getLoopForBlock(BlockNode block) {
|
||||||
|
if (loops.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (LoopInfo loop : loops) {
|
for (LoopInfo loop : loops) {
|
||||||
if (loop.getLoopBlocks().contains(block)) {
|
if (loop.getLoopBlocks().contains(block)) {
|
||||||
return loop;
|
return loop;
|
||||||
@@ -416,10 +426,27 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
return null;
|
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() {
|
public int getLoopsCount() {
|
||||||
return loops.size();
|
return loops.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<LoopInfo> getLoops() {
|
||||||
|
return loops;
|
||||||
|
}
|
||||||
|
|
||||||
public ExceptionHandler addExceptionHandler(ExceptionHandler handler) {
|
public ExceptionHandler addExceptionHandler(ExceptionHandler handler) {
|
||||||
if (exceptionHandlers.isEmpty()) {
|
if (exceptionHandlers.isEmpty()) {
|
||||||
exceptionHandlers = new ArrayList<ExceptionHandler>(2);
|
exceptionHandlers = new ArrayList<ExceptionHandler>(2);
|
||||||
@@ -467,6 +494,24 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
return false;
|
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() {
|
public int getRegsCount() {
|
||||||
return regsCount;
|
return regsCount;
|
||||||
}
|
}
|
||||||
@@ -475,7 +520,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
|
|||||||
return debugInfoOffset;
|
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);
|
SSAVar var = new SSAVar(regNum, versions[regNum], arg);
|
||||||
versions[regNum]++;
|
versions[regNum]++;
|
||||||
if (sVars.isEmpty()) {
|
if (sVars.isEmpty()) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package jadx.core.dex.nodes.parser;
|
|||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
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.DexNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -112,8 +113,9 @@ public class DebugInfoParser {
|
|||||||
int regNum = section.readUleb128();
|
int regNum = section.readUleb128();
|
||||||
LocalVar var = locals[regNum];
|
LocalVar var = locals[regNum];
|
||||||
if (var != null) {
|
if (var != null) {
|
||||||
var.end(addr, line);
|
if (var.end(addr, line)) {
|
||||||
setVar(var);
|
setVar(var);
|
||||||
|
}
|
||||||
var.start(addr, line);
|
var.start(addr, line);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -160,7 +162,7 @@ public class DebugInfoParser {
|
|||||||
|
|
||||||
for (LocalVar var : locals) {
|
for (LocalVar var : locals) {
|
||||||
if (var != null && !var.isEnd()) {
|
if (var != null && !var.isEnd()) {
|
||||||
var.end(addr, line);
|
var.end(mth.getCodeSize() - 1, line);
|
||||||
setVar(var);
|
setVar(var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,6 +171,8 @@ public class DebugInfoParser {
|
|||||||
|
|
||||||
private int addrChange(int addr, int addrInc, int line) {
|
private int addrChange(int addr, int addrInc, int line) {
|
||||||
int newAddr = addr + addrInc;
|
int newAddr = addr + addrInc;
|
||||||
|
int maxAddr = insnByOffset.length - 1;
|
||||||
|
newAddr = Math.min(newAddr, maxAddr);
|
||||||
for (int i = addr + 1; i <= newAddr; i++) {
|
for (int i = addr + 1; i <= newAddr; i++) {
|
||||||
InsnNode insn = insnByOffset[i];
|
InsnNode insn = insnByOffset[i];
|
||||||
if (insn == null) {
|
if (insn == null) {
|
||||||
@@ -236,7 +240,27 @@ public class DebugInfoParser {
|
|||||||
if (arg != null && arg.isRegister()) {
|
if (arg != null && arg.isRegister()) {
|
||||||
RegisterArg reg = (RegisterArg) arg;
|
RegisterArg reg = (RegisterArg) arg;
|
||||||
if (var.getRegNum() == reg.getRegNum()) {
|
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) {
|
public LocalVar(DexNode dex, int rn, int nameId, int typeId, int signId) {
|
||||||
this.regNum = rn;
|
this.regNum = rn;
|
||||||
String name = (nameId == DexNode.NO_INDEX ? null : dex.getString(nameId));
|
String name = nameId == DexNode.NO_INDEX ? null : dex.getString(nameId);
|
||||||
ArgType type = (typeId == DexNode.NO_INDEX ? null : dex.getType(typeId));
|
ArgType type = typeId == DexNode.NO_INDEX ? null : dex.getType(typeId);
|
||||||
String sign = (signId == DexNode.NO_INDEX ? null : dex.getString(signId));
|
String sign = signId == DexNode.NO_INDEX ? null : dex.getString(signId);
|
||||||
|
|
||||||
init(name, type, sign);
|
init(name, type, sign);
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ final class LocalVar {
|
|||||||
type = gType;
|
type = gType;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} 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;
|
this.name = name;
|
||||||
@@ -56,10 +56,8 @@ final class LocalVar {
|
|||||||
LOG.warn("Generic type in debug info not equals: {} != {}", type, gType);
|
LOG.warn("Generic type in debug info not equals: {} != {}", type, gType);
|
||||||
}
|
}
|
||||||
apply = true;
|
apply = true;
|
||||||
} else if (el.isGenericType()) {
|
|
||||||
apply = true;
|
|
||||||
} else {
|
} else {
|
||||||
apply = false;
|
apply = el.isGenericType();
|
||||||
}
|
}
|
||||||
return apply;
|
return apply;
|
||||||
}
|
}
|
||||||
@@ -69,9 +67,20 @@ final class LocalVar {
|
|||||||
this.startAddr = addr;
|
this.startAddr = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void end(int addr, int line) {
|
/**
|
||||||
this.isEnd = true;
|
* Sets end address of local variable
|
||||||
this.endAddr = addr;
|
*
|
||||||
|
* @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() {
|
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.IfNode;
|
||||||
import jadx.core.dex.instructions.IfOp;
|
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.IfNode;
|
||||||
import jadx.core.dex.instructions.IfOp;
|
import jadx.core.dex.instructions.IfOp;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
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.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
@@ -22,6 +21,7 @@ public final class IfCondition {
|
|||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
COMPARE,
|
COMPARE,
|
||||||
|
TERNARY,
|
||||||
NOT,
|
NOT,
|
||||||
AND,
|
AND,
|
||||||
OR
|
OR
|
||||||
@@ -64,6 +64,10 @@ public final class IfCondition {
|
|||||||
return new IfCondition(new Compare(insn));
|
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) {
|
public static IfCondition merge(Mode mode, IfCondition a, IfCondition b) {
|
||||||
if (a.getMode() == mode) {
|
if (a.getMode() == mode) {
|
||||||
IfCondition n = new IfCondition(a);
|
IfCondition n = new IfCondition(a);
|
||||||
@@ -89,6 +93,10 @@ public final class IfCondition {
|
|||||||
return args.get(1);
|
return args.get(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IfCondition third() {
|
||||||
|
return args.get(2);
|
||||||
|
}
|
||||||
|
|
||||||
public void addArg(IfCondition c) {
|
public void addArg(IfCondition c) {
|
||||||
args.add(c);
|
args.add(c);
|
||||||
}
|
}
|
||||||
@@ -106,6 +114,8 @@ public final class IfCondition {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case COMPARE:
|
case COMPARE:
|
||||||
return new IfCondition(cond.getCompare().invert());
|
return new IfCondition(cond.getCompare().invert());
|
||||||
|
case TERNARY:
|
||||||
|
return ternary(not(cond.first()), cond.third(), cond.second());
|
||||||
case NOT:
|
case NOT:
|
||||||
return cond.first();
|
return cond.first();
|
||||||
case AND:
|
case AND:
|
||||||
@@ -154,7 +164,10 @@ public final class IfCondition {
|
|||||||
cond = new IfCondition(cond.getMode(), args);
|
cond = new IfCondition(cond.getMode(), args);
|
||||||
}
|
}
|
||||||
if (cond.getMode() == Mode.NOT && cond.first().getMode() == Mode.NOT) {
|
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
|
// for condition with a lot of negations => make invert
|
||||||
@@ -195,14 +208,7 @@ public final class IfCondition {
|
|||||||
public List<RegisterArg> getRegisterArgs() {
|
public List<RegisterArg> getRegisterArgs() {
|
||||||
List<RegisterArg> list = new LinkedList<RegisterArg>();
|
List<RegisterArg> list = new LinkedList<RegisterArg>();
|
||||||
if (mode == Mode.COMPARE) {
|
if (mode == Mode.COMPARE) {
|
||||||
InsnArg a = compare.getA();
|
compare.getInsn().getRegisterArgs(list);
|
||||||
if (a.isRegister()) {
|
|
||||||
list.add((RegisterArg) a);
|
|
||||||
}
|
|
||||||
InsnArg b = compare.getB();
|
|
||||||
if (b.isRegister()) {
|
|
||||||
list.add((RegisterArg) b);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (IfCondition arg : args) {
|
for (IfCondition arg : args) {
|
||||||
list.addAll(arg.getRegisterArgs());
|
list.addAll(arg.getRegisterArgs());
|
||||||
@@ -216,6 +222,8 @@ public final class IfCondition {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case COMPARE:
|
case COMPARE:
|
||||||
return compare.toString();
|
return compare.toString();
|
||||||
|
case TERNARY:
|
||||||
|
return first() + " ? " + second() + " : " + third();
|
||||||
case NOT:
|
case NOT:
|
||||||
return "!" + first();
|
return "!" + first();
|
||||||
case AND:
|
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;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
|
|
||||||
@@ -7,34 +7,51 @@ import java.util.Set;
|
|||||||
|
|
||||||
public final class IfInfo {
|
public final class IfInfo {
|
||||||
private final IfCondition condition;
|
private final IfCondition condition;
|
||||||
private final Set<BlockNode> mergedBlocks = new HashSet<BlockNode>();
|
private final Set<BlockNode> mergedBlocks;
|
||||||
private final BlockNode thenBlock;
|
private final BlockNode thenBlock;
|
||||||
private final BlockNode elseBlock;
|
private final BlockNode elseBlock;
|
||||||
|
private final Set<BlockNode> skipBlocks;
|
||||||
private BlockNode outBlock;
|
private BlockNode outBlock;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private BlockNode ifBlock;
|
private BlockNode ifBlock;
|
||||||
|
|
||||||
public IfInfo(IfCondition condition, BlockNode thenBlock, BlockNode elseBlock) {
|
public IfInfo(IfCondition condition, BlockNode thenBlock, BlockNode elseBlock) {
|
||||||
this.condition = condition;
|
this(condition, thenBlock, elseBlock, new HashSet<BlockNode>(), new HashSet<BlockNode>());
|
||||||
this.thenBlock = thenBlock;
|
|
||||||
this.elseBlock = elseBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IfInfo(IfCondition condition, IfInfo info) {
|
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.condition = condition;
|
||||||
this.thenBlock = info.getThenBlock();
|
this.thenBlock = thenBlock;
|
||||||
this.elseBlock = info.getElseBlock();
|
this.elseBlock = elseBlock;
|
||||||
this.mergedBlocks.addAll(info.getMergedBlocks());
|
this.mergedBlocks = mergedBlocks;
|
||||||
|
this.skipBlocks = skipBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IfInfo invert(IfInfo info) {
|
public static IfInfo invert(IfInfo info) {
|
||||||
IfInfo tmpIf = new IfInfo(IfCondition.invert(info.getCondition()),
|
IfCondition invertedCondition = IfCondition.invert(info.getCondition());
|
||||||
info.getElseBlock(), info.getThenBlock());
|
IfInfo tmpIf = new IfInfo(invertedCondition,
|
||||||
|
info.getElseBlock(), info.getThenBlock(),
|
||||||
|
info.getMergedBlocks(), info.getSkipBlocks());
|
||||||
tmpIf.setIfBlock(info.getIfBlock());
|
tmpIf.setIfBlock(info.getIfBlock());
|
||||||
tmpIf.getMergedBlocks().addAll(info.getMergedBlocks());
|
|
||||||
return tmpIf;
|
return tmpIf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void merge(IfInfo... arr) {
|
||||||
|
for (IfInfo info : arr) {
|
||||||
|
mergedBlocks.addAll(info.getMergedBlocks());
|
||||||
|
skipBlocks.addAll(info.getSkipBlocks());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IfCondition getCondition() {
|
public IfCondition getCondition() {
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
@@ -43,6 +60,10 @@ public final class IfInfo {
|
|||||||
return mergedBlocks;
|
return mergedBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<BlockNode> getSkipBlocks() {
|
||||||
|
return skipBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
public BlockNode getThenBlock() {
|
public BlockNode getThenBlock() {
|
||||||
return thenBlock;
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
|
import jadx.core.dex.regions.AbstractRegion;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -16,8 +17,6 @@ public final class IfRegion extends AbstractRegion {
|
|||||||
private IContainer thenRegion;
|
private IContainer thenRegion;
|
||||||
private IContainer elseRegion;
|
private IContainer elseRegion;
|
||||||
|
|
||||||
private TernaryRegion ternRegion;
|
|
||||||
|
|
||||||
public IfRegion(IRegion parent, BlockNode header) {
|
public IfRegion(IRegion parent, BlockNode header) {
|
||||||
super(parent);
|
super(parent);
|
||||||
assert header.getInstructions().size() == 1;
|
assert header.getInstructions().size() == 1;
|
||||||
@@ -53,14 +52,6 @@ public final class IfRegion extends AbstractRegion {
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTernRegion(TernaryRegion ternRegion) {
|
|
||||||
this.ternRegion = ternRegion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TernaryRegion getTernRegion() {
|
|
||||||
return ternRegion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean simplifyCondition() {
|
public boolean simplifyCondition() {
|
||||||
IfCondition cond = IfCondition.simplify(condition);
|
IfCondition cond = IfCondition.simplify(condition);
|
||||||
if (cond != condition) {
|
if (cond != condition) {
|
||||||
@@ -87,10 +78,7 @@ public final class IfRegion extends AbstractRegion {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IContainer> getSubBlocks() {
|
public List<IContainer> getSubBlocks() {
|
||||||
if (ternRegion != null) {
|
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||||
return ternRegion.getSubBlocks();
|
|
||||||
}
|
|
||||||
ArrayList<IContainer> all = new ArrayList<IContainer>(3);
|
|
||||||
all.add(header);
|
all.add(header);
|
||||||
if (thenRegion != null) {
|
if (thenRegion != null) {
|
||||||
all.add(thenRegion);
|
all.add(thenRegion);
|
||||||
@@ -116,9 +104,6 @@ public final class IfRegion extends AbstractRegion {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String baseString() {
|
public String baseString() {
|
||||||
if (ternRegion != null) {
|
|
||||||
return ternRegion.baseString();
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (thenRegion != null) {
|
if (thenRegion != null) {
|
||||||
sb.append(thenRegion.baseString());
|
sb.append(thenRegion.baseString());
|
||||||
@@ -131,9 +116,6 @@ public final class IfRegion extends AbstractRegion {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (ternRegion != null) {
|
|
||||||
return ternRegion.toString();
|
|
||||||
}
|
|
||||||
return "IF(" + condition + ") then " + thenRegion + " else " + elseRegion;
|
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.IfNode;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -13,21 +16,29 @@ import java.util.List;
|
|||||||
|
|
||||||
public final class LoopRegion extends AbstractRegion {
|
public final class LoopRegion extends AbstractRegion {
|
||||||
|
|
||||||
|
private final LoopInfo info;
|
||||||
// loop header contains one 'if' insn, equals null for infinite loop
|
// loop header contains one 'if' insn, equals null for infinite loop
|
||||||
private IfCondition condition;
|
private IfCondition condition;
|
||||||
private final BlockNode conditionBlock;
|
private final BlockNode conditionBlock;
|
||||||
// instruction which must be executed before condition in every loop
|
// instruction which must be executed before condition in every loop
|
||||||
private BlockNode preCondition;
|
private BlockNode preCondition;
|
||||||
private IContainer body;
|
private IRegion body;
|
||||||
private final boolean conditionAtEnd;
|
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);
|
super(parent);
|
||||||
|
this.info = info;
|
||||||
this.conditionBlock = header;
|
this.conditionBlock = header;
|
||||||
this.condition = IfCondition.fromIfBlock(header);
|
this.condition = IfCondition.fromIfBlock(header);
|
||||||
this.conditionAtEnd = reversed;
|
this.conditionAtEnd = reversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoopInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
public IfCondition getCondition() {
|
public IfCondition getCondition() {
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
@@ -40,11 +51,11 @@ public final class LoopRegion extends AbstractRegion {
|
|||||||
return conditionBlock;
|
return conditionBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IContainer getBody() {
|
public IRegion getBody() {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBody(IContainer body) {
|
public void setBody(IRegion body) {
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,25 +88,24 @@ public final class LoopRegion extends AbstractRegion {
|
|||||||
InsnNode insn = insns.get(i);
|
InsnNode insn = insns.get(i);
|
||||||
if (insn.getResult() == null) {
|
if (insn.getResult() == null) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
RegisterArg res = insn.getResult();
|
RegisterArg res = insn.getResult();
|
||||||
if (res.getSVar().getUseCount() > 1) {
|
if (res.getSVar().getUseCount() > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
// search result arg in other insns
|
// search result arg in other insns
|
||||||
for (int j = i + 1; j < size; j++) {
|
for (int j = i + 1; j < size; j++) {
|
||||||
if (insns.get(i).containsArg(res)) {
|
if (insns.get(i).containsArg(res)) {
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// or in if insn
|
|
||||||
if (!found && ifInsn.containsArg(res)) {
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (!found) {
|
}
|
||||||
return false;
|
// or in if insn
|
||||||
}
|
if (!found && ifInsn.containsArg(res)) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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
|
@Override
|
||||||
public List<IContainer> getSubBlocks() {
|
public List<IContainer> getSubBlocks() {
|
||||||
List<IContainer> all = new ArrayList<IContainer>(3);
|
List<IContainer> all = new ArrayList<IContainer>(3);
|
||||||
@@ -131,6 +149,11 @@ public final class LoopRegion extends AbstractRegion {
|
|||||||
return Collections.unmodifiableList(all);
|
return Collections.unmodifiableList(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String baseString() {
|
public String baseString() {
|
||||||
return body.baseString();
|
return body.baseString();
|
||||||
@@ -138,6 +161,6 @@ public final class LoopRegion extends AbstractRegion {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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.Consts;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.utils.InsnUtils;
|
import jadx.core.utils.InsnUtils;
|
||||||
@@ -18,7 +18,7 @@ public class ExceptionHandler {
|
|||||||
private BlockNode handlerBlock;
|
private BlockNode handlerBlock;
|
||||||
private final List<BlockNode> blocks = new ArrayList<BlockNode>();
|
private final List<BlockNode> blocks = new ArrayList<BlockNode>();
|
||||||
private IContainer handlerRegion;
|
private IContainer handlerRegion;
|
||||||
private NamedArg arg;
|
private InsnArg arg;
|
||||||
|
|
||||||
private TryCatchBlock tryBlock;
|
private TryCatchBlock tryBlock;
|
||||||
|
|
||||||
@@ -63,11 +63,11 @@ public class ExceptionHandler {
|
|||||||
this.handlerRegion = handlerRegion;
|
this.handlerRegion = handlerRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NamedArg getArg() {
|
public InsnArg getArg() {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setArg(NamedArg arg) {
|
public void setArg(InsnArg arg) {
|
||||||
this.arg = 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.ExceptionHandler;
|
||||||
import jadx.core.dex.trycatch.SplitterBlockAttr;
|
import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||||
import jadx.core.utils.BlockUtils;
|
import jadx.core.utils.BlockUtils;
|
||||||
import jadx.core.utils.EmptyBitSet;
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -28,6 +27,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static jadx.core.utils.EmptyBitSet.EMPTY;
|
||||||
|
|
||||||
public class BlockMakerVisitor extends AbstractVisitor {
|
public class BlockMakerVisitor extends AbstractVisitor {
|
||||||
|
|
||||||
// leave these instructions alone in block node
|
// leave these instructions alone in block node
|
||||||
@@ -36,9 +37,8 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
InsnType.IF,
|
InsnType.IF,
|
||||||
InsnType.SWITCH,
|
InsnType.SWITCH,
|
||||||
InsnType.MONITOR_ENTER,
|
InsnType.MONITOR_ENTER,
|
||||||
InsnType.MONITOR_EXIT);
|
InsnType.MONITOR_EXIT
|
||||||
|
);
|
||||||
private static final BitSet EMPTY_BITSET = new EmptyBitSet();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodNode mth) {
|
public void visit(MethodNode mth) {
|
||||||
@@ -103,7 +103,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
// add this insn in new block
|
// add this insn in new block
|
||||||
block = startNewBlock(mth, -1);
|
block = startNewBlock(mth, -1);
|
||||||
curBlock.add(AFlag.SYNTHETIC);
|
curBlock.add(AFlag.SYNTHETIC);
|
||||||
block.addAttr(new SplitterBlockAttr(curBlock));
|
SplitterBlockAttr splitter = new SplitterBlockAttr(curBlock);
|
||||||
|
block.addAttr(splitter);
|
||||||
|
curBlock.addAttr(splitter);
|
||||||
connect(curBlock, block);
|
connect(curBlock, block);
|
||||||
curBlock = block;
|
curBlock = block;
|
||||||
} else {
|
} else {
|
||||||
@@ -131,12 +133,16 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
// get synthetic block for handlers
|
// get synthetic block for handlers
|
||||||
SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK);
|
SplitterBlockAttr spl = block.get(AType.SPLITTER_BLOCK);
|
||||||
if (catches != null && spl != null) {
|
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()) {
|
for (ExceptionHandler h : catches.getTryBlock().getHandlers()) {
|
||||||
BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap);
|
BlockNode handlerBlock = getBlock(h.getHandleOffset(), blocksMap);
|
||||||
// skip self loop in handler
|
// skip self loop in handler
|
||||||
if (connBlock != destBlock) {
|
if (splitterBlock != handlerBlock) {
|
||||||
connect(connBlock, destBlock);
|
connect(splitterBlock, handlerBlock);
|
||||||
|
}
|
||||||
|
if (tryEnd) {
|
||||||
|
connect(block, handlerBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +196,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
computeDominanceFrontier(mth);
|
computeDominanceFrontier(mth);
|
||||||
registerLoops(mth);
|
registerLoops(mth);
|
||||||
|
processNestedLoops(mth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlockNode getBlock(int offset, Map<Integer, BlockNode> blocksMap) {
|
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) {
|
private static void computeDominanceFrontier(MethodNode mth) {
|
||||||
for (BlockNode exit : mth.getExitBlocks()) {
|
for (BlockNode exit : mth.getExitBlocks()) {
|
||||||
exit.setDomFrontier(EMPTY_BITSET);
|
exit.setDomFrontier(EMPTY);
|
||||||
}
|
}
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
computeBlockDF(mth, block);
|
computeBlockDF(mth, block);
|
||||||
@@ -323,7 +330,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (domFrontier == null || domFrontier.cardinality() == 0) {
|
if (domFrontier == null || domFrontier.cardinality() == 0) {
|
||||||
domFrontier = EMPTY_BITSET;
|
domFrontier = EMPTY;
|
||||||
}
|
}
|
||||||
block.setDomFrontier(domFrontier);
|
block.setDomFrontier(domFrontier);
|
||||||
}
|
}
|
||||||
@@ -357,15 +364,40 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
private static void registerLoops(MethodNode mth) {
|
private static void registerLoops(MethodNode mth) {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
List<LoopInfo> loops = block.getAll(AType.LOOP);
|
|
||||||
if (block.contains(AFlag.LOOP_START)) {
|
if (block.contains(AFlag.LOOP_START)) {
|
||||||
for (LoopInfo loop : loops) {
|
for (LoopInfo loop : block.getAll(AType.LOOP)) {
|
||||||
mth.registerLoop(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) {
|
private static boolean modifyBlocksTree(MethodNode mth) {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
if (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {
|
if (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {
|
||||||
@@ -395,9 +427,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// insert additional blocks if loop has several exits
|
|
||||||
if (loops.size() == 1) {
|
if (loops.size() == 1) {
|
||||||
LoopInfo loop = loops.get(0);
|
LoopInfo loop = loops.get(0);
|
||||||
|
// insert additional blocks for possible 'break' insertion
|
||||||
List<Edge> edges = loop.getExitEdges();
|
List<Edge> edges = loop.getExitEdges();
|
||||||
if (!edges.isEmpty()) {
|
if (!edges.isEmpty()) {
|
||||||
boolean change = false;
|
boolean change = false;
|
||||||
@@ -412,6 +444,21 @@ public class BlockMakerVisitor extends AbstractVisitor {
|
|||||||
return true;
|
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);
|
return splitReturn(mth);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.instructions.IfNode;
|
import jadx.core.dex.instructions.IfNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
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.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
@@ -43,30 +44,25 @@ public class BlockProcessingHelper {
|
|||||||
* Set exception handler attribute for whole block
|
* Set exception handler attribute for whole block
|
||||||
*/
|
*/
|
||||||
private static void markExceptionHandlers(BlockNode block) {
|
private static void markExceptionHandlers(BlockNode block) {
|
||||||
if (!block.getInstructions().isEmpty()) {
|
if (block.getInstructions().isEmpty()) {
|
||||||
InsnNode me = block.getInstructions().get(0);
|
return;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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) {
|
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();
|
List<InsnNode> insns = mth.getBasicBlocks().get(0).getInstructions();
|
||||||
if (insns.size() == 1 && insns.get(0).getType() == InsnType.CONSTRUCTOR) {
|
if (insns.size() == 1 && insns.get(0).getType() == InsnType.CONSTRUCTOR) {
|
||||||
ConstructorInsn constr = (ConstructorInsn) insns.get(0);
|
ConstructorInsn constr = (ConstructorInsn) insns.get(0);
|
||||||
if (constr.isThis() && mth.getArguments(false).size() >= 1) {
|
if (constr.isThis() && !mth.getArguments(false).isEmpty()) {
|
||||||
mth.removeFirstArgument();
|
mth.removeFirstArgument();
|
||||||
mth.add(AFlag.DONT_GENERATE);
|
mth.add(AFlag.DONT_GENERATE);
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ public class ClassModifier extends AbstractVisitor {
|
|||||||
|
|
||||||
private static boolean allBlocksEmpty(List<BlockNode> blocks) {
|
private static boolean allBlocksEmpty(List<BlockNode> blocks) {
|
||||||
for (BlockNode block : blocks) {
|
for (BlockNode block : blocks) {
|
||||||
if (block.getInstructions().size() != 0) {
|
if (!block.getInstructions().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import jadx.core.dex.nodes.BlockNode;
|
|||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.BlockUtils;
|
import jadx.core.utils.BlockUtils;
|
||||||
|
import jadx.core.utils.EmptyBitSet;
|
||||||
import jadx.core.utils.InsnList;
|
import jadx.core.utils.InsnList;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<RegisterArg> getArgs(InsnNode insn) {
|
public static List<RegisterArg> getArgs(InsnNode insn) {
|
||||||
LinkedList<RegisterArg> args = new LinkedList<RegisterArg>();
|
List<RegisterArg> args = new LinkedList<RegisterArg>();
|
||||||
addArgs(insn, args);
|
addArgs(insn, args);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,8 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean canMove(int from, int to) {
|
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;
|
int start = from + 1;
|
||||||
if (start == to) {
|
if (start == to) {
|
||||||
// previous instruction or on edge of inline border
|
// previous instruction or on edge of inline border
|
||||||
@@ -105,9 +107,17 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
if (start > to) {
|
if (start > to) {
|
||||||
throw new JadxRuntimeException("Invalid inline insn positions: " + start + " - " + to);
|
throw new JadxRuntimeException("Invalid inline insn positions: " + start + " - " + to);
|
||||||
}
|
}
|
||||||
BitSet args = new BitSet();
|
BitSet movedSet;
|
||||||
for (RegisterArg arg : movedArgs) {
|
if (movedArgs.isEmpty()) {
|
||||||
args.set(arg.getRegNum());
|
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++) {
|
for (int i = start; i < to; i++) {
|
||||||
ArgsInfo argsInfo = argsList.get(i);
|
ArgsInfo argsInfo = argsList.get(i);
|
||||||
@@ -115,7 +125,7 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
InsnNode curInsn = argsInfo.insn;
|
InsnNode curInsn = argsInfo.insn;
|
||||||
if (!curInsn.canReorder() || usedArgAssign(curInsn, args)) {
|
if (!curInsn.canReorder() || usedArgAssign(curInsn, movedSet)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,21 +197,22 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
List<WrapInfo> wrapList = new ArrayList<WrapInfo>();
|
List<WrapInfo> wrapList = new ArrayList<WrapInfo>();
|
||||||
for (ArgsInfo argsInfo : argsList) {
|
for (ArgsInfo argsInfo : argsList) {
|
||||||
List<RegisterArg> args = argsInfo.getArgs();
|
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();
|
RegisterArg arg = it.previous();
|
||||||
// if (arg.getName() != null) {
|
// if (arg.getName() != null) {
|
||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
SSAVar sVar = arg.getSVar();
|
SSAVar sVar = arg.getSVar();
|
||||||
if (sVar.getAssign() == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// allow inline only one use arg or 'this'
|
// allow inline only one use arg or 'this'
|
||||||
if (sVar.getVariableUseCount() != 1 && !arg.isThis()) {
|
if (sVar.getVariableUseCount() != 1 && !arg.isThis()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
InsnNode assignInsn = sVar.getAssign().getParentInsn();
|
InsnNode assignInsn = sVar.getAssign().getParentInsn();
|
||||||
if (assignInsn == null) {
|
if (assignInsn == null || assignInsn.contains(AFlag.DONT_INLINE)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int assignPos = insnList.getIndex(assignInsn);
|
int assignPos = insnList.getIndex(assignInsn);
|
||||||
@@ -214,6 +225,7 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
// another block
|
// another block
|
||||||
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);
|
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);
|
||||||
if (assignBlock != null
|
if (assignBlock != null
|
||||||
|
&& assignInsn != arg.getParentInsn()
|
||||||
&& canMoveBetweenBlocks(assignInsn, assignBlock, block, argsInfo.getInsn())) {
|
&& canMoveBetweenBlocks(assignInsn, assignBlock, block, argsInfo.getInsn())) {
|
||||||
arg.wrapInstruction(assignInsn);
|
arg.wrapInstruction(assignInsn);
|
||||||
InsnList.remove(assignBlock, assignInsn);
|
InsnList.remove(assignBlock, assignInsn);
|
||||||
@@ -232,7 +244,7 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean canMoveBetweenBlocks(InsnNode assignInsn, BlockNode assignBlock,
|
private static boolean canMoveBetweenBlocks(InsnNode assignInsn, BlockNode assignBlock,
|
||||||
BlockNode useBlock, InsnNode useInsn) {
|
BlockNode useBlock, InsnNode useInsn) {
|
||||||
if (!BlockUtils.isPathExists(assignBlock, useBlock)) {
|
if (!BlockUtils.isPathExists(assignBlock, useBlock)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -292,7 +304,7 @@ public class CodeShrinker extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// remove method args
|
// remove method args
|
||||||
if (list.size() != 0 && args.size() != 0) {
|
if (!list.isEmpty() && !args.isEmpty()) {
|
||||||
list.removeAll(args);
|
list.removeAll(args);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.instructions.IndexInsnNode;
|
import jadx.core.dex.instructions.IndexInsnNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.InvokeNode;
|
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.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.BlockUtils;
|
|
||||||
import jadx.core.utils.InstructionRemover;
|
import jadx.core.utils.InstructionRemover;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
|||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
toRemove.clear();
|
toRemove.clear();
|
||||||
for (InsnNode insn : block.getInstructions()) {
|
for (InsnNode insn : block.getInstructions()) {
|
||||||
if (checkInsn(mth, block, insn)) {
|
if (checkInsn(mth, insn)) {
|
||||||
toRemove.add(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) {
|
if (insn.getType() != InsnType.CONST) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -48,28 +49,52 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
|||||||
if (!arg.isLiteral()) {
|
if (!arg.isLiteral()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
long lit = ((LiteralArg) arg).getLiteral();
|
||||||
|
|
||||||
SSAVar sVar = insn.getResult().getSVar();
|
SSAVar sVar = insn.getResult().getSVar();
|
||||||
if (mth.getExceptionHandlersCount() != 0) {
|
if (lit == 0) {
|
||||||
for (RegisterArg useArg : sVar.getUseList()) {
|
if (checkObjectInline(sVar)) {
|
||||||
InsnNode parentInsn = useArg.getParentInsn();
|
if (sVar.getUseCount() == 1) {
|
||||||
if (parentInsn != null) {
|
insn.getResult().getAssignInsn().add(AFlag.DONT_INLINE);
|
||||||
// TODO: speed up expensive operations
|
|
||||||
BlockNode useBlock = BlockUtils.getBlockByInsn(mth, parentInsn);
|
|
||||||
if (!BlockUtils.isCleanPathExists(block, useBlock)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
ArgType resType = insn.getResult().getType();
|
ArgType resType = insn.getResult().getType();
|
||||||
// make sure arg has correct type
|
// make sure arg has correct type
|
||||||
if (!arg.getType().isTypeKnown()) {
|
if (!arg.getType().isTypeKnown()) {
|
||||||
arg.merge(resType);
|
arg.merge(resType);
|
||||||
}
|
}
|
||||||
long lit = ((LiteralArg) arg).getLiteral();
|
|
||||||
return replaceConst(mth, sVar, lit);
|
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) {
|
private static boolean replaceConst(MethodNode mth, SSAVar sVar, long literal) {
|
||||||
List<RegisterArg> use = new ArrayList<RegisterArg>(sVar.getUseList());
|
List<RegisterArg> use = new ArrayList<RegisterArg>(sVar.getUseList());
|
||||||
int replaceCount = 0;
|
int replaceCount = 0;
|
||||||
@@ -85,6 +110,10 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
|||||||
if (use.size() == 1 || arg.isTypeImmutable()) {
|
if (use.size() == 1 || arg.isTypeImmutable()) {
|
||||||
// arg used only in one place
|
// arg used only in one place
|
||||||
litArg = InsnArg.lit(literal, arg.getType());
|
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 {
|
} else {
|
||||||
// in most cases type not equal arg.getType()
|
// in most cases type not equal arg.getType()
|
||||||
// just set unknown type and run type fixer
|
// just set unknown type and run type fixer
|
||||||
@@ -150,7 +179,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
|||||||
InvokeNode inv = (InvokeNode) insn;
|
InvokeNode inv = (InvokeNode) insn;
|
||||||
List<ArgType> types = inv.getCallMth().getArgumentsTypes();
|
List<ArgType> types = inv.getCallMth().getArgumentsTypes();
|
||||||
int count = insn.getArgsCount();
|
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++) {
|
for (int i = 0; i < count; i++) {
|
||||||
InsnArg arg = insn.getArg(i);
|
InsnArg arg = insn.getArg(i);
|
||||||
if (!arg.getType().isTypeKnown()) {
|
if (!arg.getType().isTypeKnown()) {
|
||||||
|
|||||||
@@ -30,16 +30,28 @@ public class DotGraphVisitor extends AbstractVisitor {
|
|||||||
private final boolean useRegions;
|
private final boolean useRegions;
|
||||||
private final boolean rawInsn;
|
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.dir = outDir;
|
||||||
this.useRegions = useRegions;
|
this.useRegions = useRegions;
|
||||||
this.rawInsn = rawInsn;
|
this.rawInsn = rawInsn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DotGraphVisitor(File outDir, boolean useRegions) {
|
|
||||||
this(outDir, useRegions, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodNode mth) {
|
public void visit(MethodNode mth) {
|
||||||
if (mth.isNoCode()) {
|
if (mth.isNoCode()) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import jadx.core.dex.nodes.ClassNode;
|
|||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -38,13 +39,12 @@ public class EnumVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
// collect enum fields, remove synthetic
|
// collect enum fields, remove synthetic
|
||||||
List<FieldNode> enumFields = new ArrayList<FieldNode>();
|
List<FieldNode> enumFields = new ArrayList<FieldNode>();
|
||||||
for (Iterator<FieldNode> it = cls.getFields().iterator(); it.hasNext(); ) {
|
for (FieldNode f : cls.getFields()) {
|
||||||
FieldNode f = it.next();
|
|
||||||
if (f.getAccessFlags().isEnum()) {
|
if (f.getAccessFlags().isEnum()) {
|
||||||
enumFields.add(f);
|
enumFields.add(f);
|
||||||
it.remove();
|
f.add(AFlag.DONT_GENERATE);
|
||||||
} else if (f.getAccessFlags().isSynthetic()) {
|
} else if (f.getAccessFlags().isSynthetic()) {
|
||||||
it.remove();
|
f.add(AFlag.DONT_GENERATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +80,8 @@ public class EnumVisitor extends AbstractVisitor {
|
|||||||
cls.addAttr(attr);
|
cls.addAttr(attr);
|
||||||
|
|
||||||
if (staticMethod == null) {
|
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
|
// for this broken enum puts found fields and mark as inconsistent
|
||||||
cls.add(AFlag.INCONSISTENT_CODE);
|
|
||||||
for (FieldNode field : enumFields) {
|
for (FieldNode field : enumFields) {
|
||||||
attr.getFields().add(new EnumField(field.getName(), 0));
|
attr.getFields().add(new EnumField(field.getName(), 0));
|
||||||
}
|
}
|
||||||
@@ -115,21 +114,17 @@ public class EnumVisitor extends AbstractVisitor {
|
|||||||
for (InsnNode insn : insns) {
|
for (InsnNode insn : insns) {
|
||||||
if (insn.getType() == InsnType.CONSTRUCTOR) {
|
if (insn.getType() == InsnType.CONSTRUCTOR) {
|
||||||
ConstructorInsn co = (ConstructorInsn) insn;
|
ConstructorInsn co = (ConstructorInsn) insn;
|
||||||
|
|
||||||
if (insn.getArgsCount() < 2) {
|
if (insn.getArgsCount() < 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassInfo clsInfo = co.getClassType();
|
ClassInfo clsInfo = co.getClassType();
|
||||||
ClassNode constrCls = cls.dex().resolveClass(clsInfo);
|
ClassNode constrCls = cls.dex().resolveClass(clsInfo);
|
||||||
if (constrCls == null) {
|
if (constrCls == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clsInfo.equals(cls.getClassInfo()) && !constrCls.getAccessFlags().isEnum()) {
|
if (!clsInfo.equals(cls.getClassInfo()) && !constrCls.getAccessFlags().isEnum()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterArg nameArg = (RegisterArg) insn.getArg(0);
|
RegisterArg nameArg = (RegisterArg) insn.getArg(0);
|
||||||
// InsnArg pos = insn.getArg(1);
|
// InsnArg pos = insn.getArg(1);
|
||||||
// TODO add check: pos == j
|
// TODO add check: pos == j
|
||||||
@@ -137,7 +132,6 @@ public class EnumVisitor extends AbstractVisitor {
|
|||||||
if (name == null) {
|
if (name == null) {
|
||||||
throw new JadxException("Unknown enum field name: " + cls);
|
throw new JadxException("Unknown enum field name: " + cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumField field = new EnumField(name, insn.getArgsCount() - 2);
|
EnumField field = new EnumField(name, insn.getArgsCount() - 2);
|
||||||
attr.getFields().add(field);
|
attr.getFields().add(field);
|
||||||
for (int i = 2; i < insn.getArgsCount(); i++) {
|
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.AFlag;
|
||||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -34,10 +33,8 @@ public class MethodInlineVisitor extends AbstractVisitor {
|
|||||||
// synthetic field getter
|
// synthetic field getter
|
||||||
BlockNode block = mth.getBasicBlocks().get(1);
|
BlockNode block = mth.getBasicBlocks().get(1);
|
||||||
InsnNode insn = block.getInstructions().get(0);
|
InsnNode insn = block.getInstructions().get(0);
|
||||||
InsnNode inl = new InsnNode(InsnType.ARGS, 1);
|
|
||||||
// set arg from 'return' instruction
|
// set arg from 'return' instruction
|
||||||
inl.addArg(insn.getArg(0));
|
addInlineAttr(mth, InsnNode.wrapArg(insn.getArg(0)));
|
||||||
addInlineAttr(mth, inl);
|
|
||||||
} else {
|
} else {
|
||||||
// synthetic field setter or method invoke
|
// synthetic field setter or method invoke
|
||||||
if (firstBlock.getInstructions().size() == 1) {
|
if (firstBlock.getInstructions().size() == 1) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
|
import jadx.core.codegen.TypeGen;
|
||||||
import jadx.core.deobf.NameMapper;
|
import jadx.core.deobf.NameMapper;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.info.MethodInfo;
|
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.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
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.RegisterArg;
|
||||||
|
import jadx.core.dex.instructions.args.SSAVar;
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
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.dex.trycatch.ExceptionHandler;
|
||||||
import jadx.core.utils.InstructionRemover;
|
import jadx.core.utils.InstructionRemover;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -41,8 +45,10 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
if (mth.isNoCode()) {
|
if (mth.isNoCode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
removeStep(mth);
|
|
||||||
replaceStep(mth);
|
InstructionRemover remover = new InstructionRemover(mth);
|
||||||
|
replaceStep(mth, remover);
|
||||||
|
removeStep(mth, remover);
|
||||||
|
|
||||||
checkArgsNames(mth);
|
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();
|
ClassNode parentClass = mth.getParentClass();
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
InstructionRemover remover = new InstructionRemover(mth, block);
|
remover.setBlock(block);
|
||||||
|
|
||||||
int size = block.getInstructions().size();
|
int size = block.getInstructions().size();
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
InsnNode insn = block.getInstructions().get(i);
|
InsnNode insn = block.getInstructions().get(i);
|
||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case INVOKE:
|
case INVOKE:
|
||||||
InvokeNode inv = (InvokeNode) insn;
|
processInvoke(mth, block, i, remover);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONST:
|
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
|
* Remove unnecessary instructions
|
||||||
*/
|
*/
|
||||||
private static void removeStep(MethodNode mth) {
|
private static void removeStep(MethodNode mth, InstructionRemover remover) {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
InstructionRemover remover = new InstructionRemover(mth, block);
|
remover.setBlock(block);
|
||||||
|
|
||||||
int size = block.getInstructions().size();
|
int size = block.getInstructions().size();
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
InsnNode insn = block.getInstructions().get(i);
|
InsnNode insn = block.getInstructions().get(i);
|
||||||
|
|
||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case NOP:
|
case NOP:
|
||||||
case GOTO:
|
case GOTO:
|
||||||
@@ -201,6 +291,7 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
ExceptionHandler excHandler = handlerAttr.getHandler();
|
ExceptionHandler excHandler = handlerAttr.getHandler();
|
||||||
boolean noExitNode = true; // check if handler has exit edge to block not from this handler
|
boolean noExitNode = true; // check if handler has exit edge to block not from this handler
|
||||||
|
boolean reThrow = false;
|
||||||
for (BlockNode excBlock : excHandler.getBlocks()) {
|
for (BlockNode excBlock : excHandler.getBlocks()) {
|
||||||
if (noExitNode) {
|
if (noExitNode) {
|
||||||
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
|
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
|
||||||
@@ -211,11 +302,11 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
if (excHandler.isCatchAll()
|
if (excHandler.isCatchAll()
|
||||||
&& size > 0
|
&& size > 0
|
||||||
&& insns.get(size - 1).getType() == InsnType.THROW) {
|
&& insns.get(size - 1).getType() == InsnType.THROW) {
|
||||||
|
reThrow = true;
|
||||||
InstructionRemover.remove(mth, excBlock, size - 1);
|
InstructionRemover.remove(mth, excBlock, size - 1);
|
||||||
|
|
||||||
// move not removed instructions to 'finally' block
|
// move not removed instructions to 'finally' block
|
||||||
if (insns.size() != 0) {
|
if (!insns.isEmpty()) {
|
||||||
// TODO: support instructions from several blocks
|
// TODO: support instructions from several blocks
|
||||||
// tryBlock.setFinalBlockFromInsns(mth, insns);
|
// tryBlock.setFinalBlockFromInsns(mth, insns);
|
||||||
// TODO: because of incomplete realization don't extract final block,
|
// TODO: because of incomplete realization don't extract final block,
|
||||||
@@ -226,19 +317,36 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<InsnNode> blockInsns = block.getInstructions();
|
List<InsnNode> blockInsns = block.getInstructions();
|
||||||
if (blockInsns.size() > 0) {
|
if (!blockInsns.isEmpty()) {
|
||||||
InsnNode insn = blockInsns.get(0);
|
InsnNode insn = blockInsns.get(0);
|
||||||
if (insn.getType() == InsnType.MOVE_EXCEPTION
|
if (insn.getType() == InsnType.MOVE_EXCEPTION) {
|
||||||
&& insn.getResult().getSVar().getUseCount() == 0) {
|
// result arg used both in this insn and exception handler,
|
||||||
InstructionRemover.remove(mth, block, 0);
|
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;
|
int totalSize = 0;
|
||||||
for (BlockNode excBlock : excHandler.getBlocks()) {
|
for (BlockNode excBlock : excHandler.getBlocks()) {
|
||||||
totalSize += excBlock.getInstructions().size();
|
totalSize += excBlock.getInstructions().size();
|
||||||
}
|
}
|
||||||
if (totalSize == 0 && noExitNode) {
|
if (totalSize == 0 && noExitNode && reThrow) {
|
||||||
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
|
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,16 +361,6 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
block.getInstructions().set(i, insn);
|
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) {
|
private static void checkArgsNames(MethodNode mth) {
|
||||||
for (RegisterArg arg : mth.getArguments(false)) {
|
for (RegisterArg arg : mth.getArguments(false)) {
|
||||||
String name = arg.getName();
|
String name = arg.getName();
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
|||||||
for (BlockNode block : blocks) {
|
for (BlockNode block : blocks) {
|
||||||
removeInstructions(block);
|
removeInstructions(block);
|
||||||
checkInline(block);
|
checkInline(block);
|
||||||
removeParenthesis(block);
|
// removeParenthesis(block);
|
||||||
modifyArith(block);
|
modifyArith(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
|||||||
case NOP:
|
case NOP:
|
||||||
case MONITOR_ENTER:
|
case MONITOR_ENTER:
|
||||||
case MONITOR_EXIT:
|
case MONITOR_EXIT:
|
||||||
|
case MOVE_EXCEPTION:
|
||||||
it.remove();
|
it.remove();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -123,25 +124,20 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
|||||||
*/
|
*/
|
||||||
private static void modifyArith(BlockNode block) {
|
private static void modifyArith(BlockNode block) {
|
||||||
List<InsnNode> list = block.getInstructions();
|
List<InsnNode> list = block.getInstructions();
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (InsnNode insn : list) {
|
||||||
InsnNode insn = list.get(i);
|
if (insn.getType() == InsnType.ARITH) {
|
||||||
if (insn.getType() != InsnType.ARITH) {
|
RegisterArg res = insn.getResult();
|
||||||
continue;
|
InsnArg arg = insn.getArg(0);
|
||||||
}
|
boolean replace = false;
|
||||||
ArithNode arith = (ArithNode) insn;
|
if (res.equals(arg)) {
|
||||||
RegisterArg res = arith.getResult();
|
replace = true;
|
||||||
InsnArg arg = arith.getArg(0);
|
} else if (arg.isRegister()) {
|
||||||
boolean replace = false;
|
RegisterArg regArg = (RegisterArg) arg;
|
||||||
|
replace = res.equalRegisterAndType(regArg);
|
||||||
if (res.equals(arg)) {
|
}
|
||||||
replace = true;
|
if (replace) {
|
||||||
} else if (arg.isRegister()) {
|
insn.add(AFlag.ARITH_ONEARG);
|
||||||
RegisterArg regArg = (RegisterArg) arg;
|
}
|
||||||
replace = res.equalRegisterAndType(regArg);
|
|
||||||
}
|
|
||||||
if (replace) {
|
|
||||||
ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1));
|
|
||||||
list.set(i, newArith);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
package jadx.core.dex.visitors;
|
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.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.InsnArg;
|
||||||
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
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.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.InstructionRemover;
|
import jadx.core.utils.InstructionRemover;
|
||||||
|
import jadx.core.utils.exceptions.DecodeException;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -43,6 +55,9 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
case NEW_ARRAY:
|
case NEW_ARRAY:
|
||||||
return processNewArray(mth, instructions, i, remover);
|
return processNewArray(mth, instructions, i, remover);
|
||||||
|
|
||||||
|
case SWITCH:
|
||||||
|
return processEnumSwitch(mth, (SwitchNode) insn);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -75,4 +90,170 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
return filledArr;
|
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.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.dex.regions.conditions.IfCondition;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -62,6 +64,9 @@ public class SimplifyVisitor extends AbstractVisitor {
|
|||||||
case IF:
|
case IF:
|
||||||
simplifyIf((IfNode) insn);
|
simplifyIf((IfNode) insn);
|
||||||
break;
|
break;
|
||||||
|
case TERNARY:
|
||||||
|
simplifyTernary((TernaryInsn) insn);
|
||||||
|
break;
|
||||||
|
|
||||||
case INVOKE:
|
case INVOKE:
|
||||||
return convertInvoke(mth, insn);
|
return convertInvoke(mth, insn);
|
||||||
@@ -99,12 +104,24 @@ public class SimplifyVisitor extends AbstractVisitor {
|
|||||||
&& ((LiteralArg) insn.getArg(1)).getLiteral() == 0) {
|
&& ((LiteralArg) insn.getArg(1)).getLiteral() == 0) {
|
||||||
insn.changeCondition(insn.getOp(), wi.getArg(0), wi.getArg(1));
|
insn.changeCondition(insn.getOp(), wi.getArg(0), wi.getArg(1));
|
||||||
} else {
|
} 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) {
|
private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) {
|
||||||
MethodInfo callMth = ((InvokeNode) insn).getCallMth();
|
MethodInfo callMth = ((InvokeNode) insn).getCallMth();
|
||||||
if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER)
|
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.IBlock;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
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.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.ErrorsCounter;
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
@@ -72,7 +72,6 @@ public class CheckRegions extends AbstractVisitor {
|
|||||||
BlockNode loopHeader = ((LoopRegion) region).getHeader();
|
BlockNode loopHeader = ((LoopRegion) region).getHeader();
|
||||||
if (loopHeader != null && loopHeader.getInstructions().size() != 1) {
|
if (loopHeader != null && loopHeader.getInstructions().size() != 1) {
|
||||||
ErrorsCounter.methodError(mth, "Incorrect condition in loop: " + loopHeader);
|
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.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
|
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||||
import jadx.core.dex.instructions.IfNode;
|
import jadx.core.dex.instructions.IfNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.regions.IfCondition;
|
import jadx.core.dex.regions.conditions.IfCondition;
|
||||||
import jadx.core.dex.regions.IfCondition.Mode;
|
import jadx.core.dex.regions.conditions.IfCondition.Mode;
|
||||||
import jadx.core.dex.regions.IfInfo;
|
import jadx.core.dex.regions.conditions.IfInfo;
|
||||||
import jadx.core.utils.BlockUtils;
|
import jadx.core.utils.BlockUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -21,6 +22,7 @@ import java.util.Set;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static jadx.core.utils.BlockUtils.getNextBlock;
|
||||||
import static jadx.core.utils.BlockUtils.isPathExists;
|
import static jadx.core.utils.BlockUtils.isPathExists;
|
||||||
|
|
||||||
public class IfMakerHelper {
|
public class IfMakerHelper {
|
||||||
@@ -38,6 +40,11 @@ public class IfMakerHelper {
|
|||||||
return info;
|
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) {
|
static IfInfo restructureIf(MethodNode mth, BlockNode block, IfInfo info) {
|
||||||
final BlockNode thenBlock = info.getThenBlock();
|
final BlockNode thenBlock = info.getThenBlock();
|
||||||
final BlockNode elseBlock = info.getElseBlock();
|
final BlockNode elseBlock = info.getElseBlock();
|
||||||
@@ -47,18 +54,18 @@ public class IfMakerHelper {
|
|||||||
info.setOutBlock(null);
|
info.setOutBlock(null);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
boolean badThen = !allPathsFromIf(thenBlock, info);
|
boolean badThen = isBadBranchBlock(info, thenBlock);
|
||||||
boolean badElse = !allPathsFromIf(elseBlock, info);
|
boolean badElse = isBadBranchBlock(info, elseBlock);
|
||||||
if (badThen && badElse) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
if (badElse) {
|
if (badElse) {
|
||||||
info = new IfInfo(info.getCondition(), thenBlock, null);
|
info = new IfInfo(info, thenBlock, null);
|
||||||
info.setOutBlock(elseBlock);
|
info.setOutBlock(elseBlock);
|
||||||
} else if (badThen) {
|
} else if (badThen) {
|
||||||
info = IfInfo.invert(info);
|
info = IfInfo.invert(info);
|
||||||
info = new IfInfo(info.getCondition(), elseBlock, null);
|
info = new IfInfo(info, elseBlock, null);
|
||||||
info.setOutBlock(thenBlock);
|
info.setOutBlock(thenBlock);
|
||||||
} else {
|
} else {
|
||||||
List<BlockNode> thenSC = thenBlock.getCleanSuccessors();
|
List<BlockNode> thenSC = thenBlock.getCleanSuccessors();
|
||||||
@@ -86,6 +93,26 @@ public class IfMakerHelper {
|
|||||||
return info;
|
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) {
|
private static boolean allPathsFromIf(BlockNode block, IfInfo info) {
|
||||||
List<BlockNode> preds = block.getPredecessors();
|
List<BlockNode> preds = block.getPredecessors();
|
||||||
Set<BlockNode> ifBlocks = info.getMergedBlocks();
|
Set<BlockNode> ifBlocks = info.getMergedBlocks();
|
||||||
@@ -126,12 +153,13 @@ public class IfMakerHelper {
|
|||||||
if (!RegionMaker.isEqualPaths(curElse, nextIf.getElseBlock())
|
if (!RegionMaker.isEqualPaths(curElse, nextIf.getElseBlock())
|
||||||
&& !RegionMaker.isEqualPaths(curThen, nextIf.getThenBlock())) {
|
&& !RegionMaker.isEqualPaths(curThen, nextIf.getThenBlock())) {
|
||||||
// complex condition, run additional checks
|
// complex condition, run additional checks
|
||||||
if (checkConditionBranches(curThen, curElse) || checkConditionBranches(curElse, curThen)) {
|
if (checkConditionBranches(curThen, curElse)
|
||||||
|
|| checkConditionBranches(curElse, curThen)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BlockNode otherBranchBlock = followThenBranch ? curElse : curThen;
|
BlockNode otherBranchBlock = followThenBranch ? curElse : curThen;
|
||||||
if (!isPathExists(nextIf.getIfBlock(), otherBranchBlock)) {
|
if (!isPathExists(nextIf.getIfBlock(), otherBranchBlock)) {
|
||||||
return null;
|
return checkForTernaryInCondition(currentIf);
|
||||||
}
|
}
|
||||||
if (isPathExists(nextIf.getThenBlock(), otherBranchBlock)
|
if (isPathExists(nextIf.getThenBlock(), otherBranchBlock)
|
||||||
&& isPathExists(nextIf.getElseBlock(), otherBranchBlock)) {
|
&& isPathExists(nextIf.getElseBlock(), otherBranchBlock)) {
|
||||||
@@ -147,15 +175,46 @@ public class IfMakerHelper {
|
|||||||
if (isInversionNeeded(currentIf, nextIf)) {
|
if (isInversionNeeded(currentIf, nextIf)) {
|
||||||
nextIf = IfInfo.invert(nextIf);
|
nextIf = IfInfo.invert(nextIf);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return currentIf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IfInfo result = mergeIfInfo(currentIf, nextIf, followThenBranch);
|
IfInfo result = mergeIfInfo(currentIf, nextIf, followThenBranch);
|
||||||
// search next nested if block
|
// search next nested if block
|
||||||
IfInfo next = mergeNestedIfNodes(result);
|
return searchNestedIf(result);
|
||||||
if (next != null) {
|
}
|
||||||
return next;
|
|
||||||
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,19 +229,33 @@ public class IfMakerHelper {
|
|||||||
|
|
||||||
private static IfInfo mergeIfInfo(IfInfo first, IfInfo second, boolean followThenBranch) {
|
private static IfInfo mergeIfInfo(IfInfo first, IfInfo second, boolean followThenBranch) {
|
||||||
Mode mergeOperation = followThenBranch ? Mode.AND : Mode.OR;
|
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());
|
IfCondition condition = IfCondition.merge(mergeOperation, first.getCondition(), second.getCondition());
|
||||||
IfInfo result = new IfInfo(condition, second);
|
IfInfo result = new IfInfo(condition, second);
|
||||||
result.setIfBlock(first.getIfBlock());
|
result.setIfBlock(first.getIfBlock());
|
||||||
result.getMergedBlocks().addAll(first.getMergedBlocks());
|
result.merge(first, second);
|
||||||
result.getMergedBlocks().addAll(second.getMergedBlocks());
|
|
||||||
|
BlockNode otherPathBlock = followThenBranch ? first.getElseBlock() : first.getThenBlock();
|
||||||
|
skipSimplePath(otherPathBlock, result.getSkipBlocks());
|
||||||
return result;
|
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) {
|
private static IfInfo getNextIf(IfInfo info, BlockNode block) {
|
||||||
if (!canSelectNext(info, block)) {
|
if (!canSelectNext(info, block)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -250,4 +323,13 @@ public class IfMakerHelper {
|
|||||||
}
|
}
|
||||||
return null;
|
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.IContainer;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
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.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.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.RegionUtils;
|
import jadx.core.utils.RegionUtils;
|
||||||
|
|
||||||
@@ -21,6 +21,16 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodNode mth) {
|
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.traverseAll(mth, this);
|
||||||
DepthRegionTraversal.traverseAllIterative(mth, this);
|
DepthRegionTraversal.traverseAllIterative(mth, this);
|
||||||
}
|
}
|
||||||
@@ -35,7 +45,7 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
|||||||
@Override
|
@Override
|
||||||
public boolean visitRegion(MethodNode mth, IRegion region) {
|
public boolean visitRegion(MethodNode mth, IRegion region) {
|
||||||
if (region instanceof IfRegion) {
|
if (region instanceof IfRegion) {
|
||||||
return removeRedundantElseBlock((IfRegion) region);
|
return removeRedundantElseBlock(mth, (IfRegion) region);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -53,8 +63,6 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
|||||||
moveReturnToThenBlock(mth, ifRegion);
|
moveReturnToThenBlock(mth, ifRegion);
|
||||||
moveBreakToThenBlock(ifRegion);
|
moveBreakToThenBlock(ifRegion);
|
||||||
markElseIfChains(ifRegion);
|
markElseIfChains(ifRegion);
|
||||||
|
|
||||||
TernaryMod.makeTernaryInsn(mth, ifRegion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void simplifyIfCondition(IfRegion ifRegion) {
|
private static void simplifyIfCondition(IfRegion ifRegion) {
|
||||||
@@ -128,28 +136,38 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean removeRedundantElseBlock(IfRegion ifRegion) {
|
private static boolean removeRedundantElseBlock(MethodNode mth, IfRegion ifRegion) {
|
||||||
if (ifRegion.getElseRegion() != null
|
if (ifRegion.getElseRegion() == null
|
||||||
&& !ifRegion.contains(AFlag.ELSE_IF_CHAIN)
|
|| ifRegion.contains(AFlag.ELSE_IF_CHAIN)
|
||||||
&& !ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN)
|
|| ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN)) {
|
||||||
&& hasBranchTerminator(ifRegion)
|
return false;
|
||||||
&& insnsCount(ifRegion.getThenRegion()) < 2) {
|
}
|
||||||
IRegion parent = ifRegion.getParent();
|
if (!hasBranchTerminator(ifRegion.getThenRegion())) {
|
||||||
Region newRegion = new Region(parent);
|
return false;
|
||||||
if (parent.replaceSubBlock(ifRegion, newRegion)) {
|
}
|
||||||
newRegion.add(ifRegion);
|
// code style check:
|
||||||
newRegion.add(ifRegion.getElseRegion());
|
// will remove 'return;' from 'then' and 'else' with one instruction
|
||||||
ifRegion.setElseRegion(null);
|
// see #jadx.tests.integration.conditions.TestConditions9
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasBranchTerminator(IfRegion ifRegion) {
|
private static boolean hasBranchTerminator(IContainer region) {
|
||||||
// TODO: check for exception throw
|
// TODO: check for exception throw
|
||||||
return RegionUtils.hasExitBlock(ifRegion.getThenRegion())
|
return RegionUtils.hasExitBlock(region)
|
||||||
|| RegionUtils.hasBreakInsn(ifRegion.getThenRegion());
|
|| RegionUtils.hasBreakInsn(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void invertIfRegion(IfRegion ifRegion) {
|
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;
|
package jadx.core.dex.visitors.regions;
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
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.nodes.MethodNode;
|
||||||
import jadx.core.dex.regions.AbstractRegion;
|
import jadx.core.dex.regions.AbstractRegion;
|
||||||
import jadx.core.dex.regions.Region;
|
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.CatchAttr;
|
||||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||||
import jadx.core.utils.BlockUtils;
|
import jadx.core.utils.BlockUtils;
|
||||||
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.RegionUtils;
|
import jadx.core.utils.RegionUtils;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
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
|
// intersect to get dominator of dominators
|
||||||
List<BlockNode> domBlocks = BlockUtils.bitSetToBlocks(mth, bs);
|
List<BlockNode> domBlocks = BlockUtils.bitSetToBlocks(mth, bs);
|
||||||
@@ -94,7 +100,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
|||||||
|
|
||||||
TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
|
TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
|
||||||
if (prevTB != null) {
|
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)) {
|
if (region.getSubBlocks().contains(dominator)) {
|
||||||
TryCatchBlock tb = tryBlocksMap.get(dominator);
|
TryCatchBlock tb = tryBlocksMap.get(dominator);
|
||||||
if (!wrapBlocks(region, tb, dominator)) {
|
if (!wrapBlocks(region, tb, dominator)) {
|
||||||
LOG.warn("Can't wrap try/catch for {}, method: {}", dominator, mth);
|
ErrorsCounter.methodError(mth, "Can't wrap try/catch for " + region);
|
||||||
mth.add(AFlag.INCONSISTENT_CODE);
|
|
||||||
}
|
}
|
||||||
tryBlocksMap.remove(dominator);
|
tryBlocksMap.remove(dominator);
|
||||||
return;
|
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
|
* 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) {
|
private static boolean wrapBlocks(IRegion replaceRegion, TryCatchBlock tb, BlockNode dominator) {
|
||||||
Region newRegion = new Region(region);
|
IRegion region = replaceRegion;
|
||||||
|
if (region instanceof LoopRegion) {
|
||||||
|
LoopRegion loop = (LoopRegion) region;
|
||||||
|
region = loop.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
Region tryRegion = new Region(region);
|
||||||
List<IContainer> subBlocks = region.getSubBlocks();
|
List<IContainer> subBlocks = region.getSubBlocks();
|
||||||
for (IContainer cont : subBlocks) {
|
for (IContainer cont : subBlocks) {
|
||||||
if (RegionUtils.isDominatedBy(dominator, cont)) {
|
if (RegionUtils.isDominatedBy(dominator, cont)) {
|
||||||
if (isHandlerPath(tb, cont)) {
|
if (isHandlerPath(tb, cont)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
newRegion.getSubBlocks().add(cont);
|
tryRegion.getSubBlocks().add(cont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newRegion.getSubBlocks().isEmpty()) {
|
if (tryRegion.getSubBlocks().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TryCatchRegion tryCatchRegion = new TryCatchRegion(region, tryRegion);
|
||||||
|
tryRegion.setParent(tryCatchRegion);
|
||||||
|
tryCatchRegion.setTryCatchBlock(tb.getCatchAttr().getTryBlock());
|
||||||
|
|
||||||
// replace first node by region
|
// replace first node by region
|
||||||
IContainer firstNode = newRegion.getSubBlocks().get(0);
|
IContainer firstNode = tryRegion.getSubBlocks().get(0);
|
||||||
if (!region.replaceSubBlock(firstNode, newRegion)) {
|
if (!region.replaceSubBlock(firstNode, tryCatchRegion)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
subBlocks.removeAll(newRegion.getSubBlocks());
|
subBlocks.removeAll(tryRegion.getSubBlocks());
|
||||||
|
|
||||||
newRegion.addAttr(tb.getCatchAttr());
|
// fix parents for tryRegion sub blocks
|
||||||
|
for (IContainer cont : tryRegion.getSubBlocks()) {
|
||||||
// fix parents
|
|
||||||
for (IContainer cont : newRegion.getSubBlocks()) {
|
|
||||||
if (cont instanceof AbstractRegion) {
|
if (cont instanceof AbstractRegion) {
|
||||||
AbstractRegion aReg = (AbstractRegion) cont;
|
AbstractRegion aReg = (AbstractRegion) cont;
|
||||||
aReg.setParent(newRegion);
|
aReg.setParent(tryRegion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import jadx.core.codegen.NameGen;
|
|||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
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.ArgType;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.args.VarName;
|
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.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.regions.IfRegion;
|
|
||||||
import jadx.core.dex.regions.SwitchRegion;
|
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.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.RegionUtils;
|
import jadx.core.utils.RegionUtils;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
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
|
@Override
|
||||||
public void visit(MethodNode mth) throws JadxException {
|
public void visit(MethodNode mth) throws JadxException {
|
||||||
if (mth.isNoCode()) {
|
if (mth.isNoCode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<Variable, Usage> usageMap = new LinkedHashMap<Variable, Usage>();
|
final Map<Variable, Usage> usageMap = new LinkedHashMap<Variable, Usage>();
|
||||||
for (RegisterArg arg : mth.getArguments(true)) {
|
for (RegisterArg arg : mth.getArguments(true)) {
|
||||||
addToUsageMap(arg, usageMap);
|
addToUsageMap(arg, usageMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all variables usage
|
// collect all variables usage
|
||||||
IRegionVisitor collect = new TracedRegionVisitor() {
|
IRegionVisitor collect = new CollectUsageRegionVisitor(usageMap);
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DepthRegionTraversal.traverseAll(mth, collect);
|
DepthRegionTraversal.traverseAll(mth, collect);
|
||||||
|
|
||||||
// reduce assigns map
|
// reduce assigns map
|
||||||
@@ -158,6 +194,22 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
usageMap.remove(new Variable(arg));
|
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()) {
|
if (usageMap.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -168,24 +220,20 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
for (Iterator<Entry<Variable, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
|
for (Iterator<Entry<Variable, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
|
||||||
Entry<Variable, Usage> entry = it.next();
|
Entry<Variable, Usage> entry = it.next();
|
||||||
Usage u = entry.getValue();
|
Usage u = entry.getValue();
|
||||||
|
|
||||||
// if no assigns => remove
|
|
||||||
if (u.getAssigns().isEmpty()) {
|
|
||||||
it.remove();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if variable can be declared at current assigns
|
// check if variable can be declared at current assigns
|
||||||
for (IRegion assignRegion : u.getAssigns()) {
|
for (IRegion assignRegion : u.getAssigns()) {
|
||||||
if (u.getArgRegion() == assignRegion
|
if (u.getArgRegion() == assignRegion
|
||||||
&& canDeclareInRegion(u, assignRegion, regionsOrder)) {
|
&& canDeclareInRegion(u, assignRegion, regionsOrder)) {
|
||||||
u.getArg().getParentInsn().add(AFlag.DECLARE_VAR);
|
if (declareAtAssign(u)) {
|
||||||
processVar(u.getArg());
|
it.remove();
|
||||||
it.remove();
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (usageMap.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// apply
|
// apply
|
||||||
for (Entry<Variable, Usage> entry : usageMap.entrySet()) {
|
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);
|
Variable varId = new Variable(arg);
|
||||||
Usage usage = usageMap.get(varId);
|
Usage usage = usageMap.get(varId);
|
||||||
if (usage == null) {
|
if (usage == null) {
|
||||||
@@ -247,6 +295,17 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
return usage;
|
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) {
|
private static void declareVar(IContainer region, RegisterArg arg) {
|
||||||
DeclareVariablesAttr dv = region.get(AType.DECLARE_VARIABLES);
|
DeclareVariablesAttr dv = region.get(AType.DECLARE_VARIABLES);
|
||||||
if (dv == null) {
|
if (dv == null) {
|
||||||
@@ -262,7 +321,7 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int calculateOrder(IContainer container, Map<IContainer, Integer> regionsOrder,
|
private static int calculateOrder(IContainer container, Map<IContainer, Integer> regionsOrder,
|
||||||
int id, boolean inc) {
|
int id, boolean inc) {
|
||||||
if (!(container instanceof IRegion)) {
|
if (!(container instanceof IRegion)) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -295,12 +354,20 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
LOG.debug("TODO: Not found order for region {} for {}", region, u);
|
LOG.debug("TODO: Not found order for region {} for {}", region, u);
|
||||||
return false;
|
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)
|
return isAllRegionsAfter(region, pos, u.getAssigns(), regionsOrder)
|
||||||
&& isAllRegionsAfter(region, pos, u.getUseRegions(), regionsOrder);
|
&& isAllRegionsAfter(region, pos, u.getUseRegions(), regionsOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAllRegionsAfter(IRegion region, Integer pos,
|
private static boolean isAllRegionsAfter(IRegion region, int pos,
|
||||||
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
|
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
|
||||||
for (IRegion r : regions) {
|
for (IRegion r : regions) {
|
||||||
if (r == region) {
|
if (r == region) {
|
||||||
continue;
|
continue;
|
||||||
@@ -313,7 +380,7 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
if (pos > rPos) {
|
if (pos > rPos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (pos.equals(rPos)) {
|
if (pos == rPos) {
|
||||||
return isAllRegionsAfterRecursive(region, regions);
|
return isAllRegionsAfterRecursive(region, regions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import jadx.core.Consts;
|
|||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
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.IfNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.SwitchNode;
|
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.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
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.Region;
|
||||||
import jadx.core.dex.regions.SwitchRegion;
|
import jadx.core.dex.regions.SwitchRegion;
|
||||||
import jadx.core.dex.regions.SynchronizedRegion;
|
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.ExcHandlerAttr;
|
||||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||||
@@ -40,8 +41,10 @@ import java.util.Set;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.makeIfInfo;
|
||||||
import static jadx.core.dex.visitors.regions.IfMakerHelper.mergeNestedIfNodes;
|
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.getBlockByOffset;
|
||||||
import static jadx.core.utils.BlockUtils.getNextBlock;
|
import static jadx.core.utils.BlockUtils.getNextBlock;
|
||||||
import static jadx.core.utils.BlockUtils.isPathExists;
|
import static jadx.core.utils.BlockUtils.isPathExists;
|
||||||
@@ -67,7 +70,7 @@ public class RegionMaker {
|
|||||||
if (Consts.DEBUG) {
|
if (Consts.DEBUG) {
|
||||||
int id = startBlock.getId();
|
int id = startBlock.getId();
|
||||||
if (processedBlocks.get(id)) {
|
if (processedBlocks.get(id)) {
|
||||||
LOG.debug(" Block already processed: " + startBlock + ", mth: " + mth);
|
LOG.debug(" Block already processed: {}, mth: {}", startBlock, mth);
|
||||||
} else {
|
} else {
|
||||||
processedBlocks.set(id);
|
processedBlocks.set(id);
|
||||||
}
|
}
|
||||||
@@ -137,9 +140,8 @@ public class RegionMaker {
|
|||||||
}
|
}
|
||||||
if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
|
if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
|
||||||
return next;
|
return next;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) {
|
private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) {
|
||||||
@@ -163,17 +165,17 @@ public class RegionMaker {
|
|||||||
|
|
||||||
LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
|
LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
|
||||||
if (loopRegion == null) {
|
if (loopRegion == null) {
|
||||||
return makeEndlessLoop(curRegion, stack, loop, loopStart);
|
BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);
|
||||||
|
insertContinue(loop);
|
||||||
|
return exit;
|
||||||
}
|
}
|
||||||
curRegion.getSubBlocks().add(loopRegion);
|
curRegion.getSubBlocks().add(loopRegion);
|
||||||
IRegion outerRegion = stack.peekRegion();
|
IRegion outerRegion = stack.peekRegion();
|
||||||
stack.push(loopRegion);
|
stack.push(loopRegion);
|
||||||
|
|
||||||
IfInfo info = makeIfInfo(loopRegion.getHeader());
|
IfInfo condInfo = makeIfInfo(loopRegion.getHeader());
|
||||||
IfInfo condInfo = mergeNestedIfNodes(info);
|
condInfo = searchNestedIf(condInfo);
|
||||||
if (condInfo == null) {
|
confirmMerge(condInfo);
|
||||||
condInfo = info;
|
|
||||||
}
|
|
||||||
if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) {
|
if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) {
|
||||||
// invert loop condition if 'then' points to exit
|
// invert loop condition if 'then' points to exit
|
||||||
condInfo = IfInfo.invert(condInfo);
|
condInfo = IfInfo.invert(condInfo);
|
||||||
@@ -181,15 +183,15 @@ public class RegionMaker {
|
|||||||
loopRegion.setCondition(condInfo.getCondition());
|
loopRegion.setCondition(condInfo.getCondition());
|
||||||
exitBlocks.removeAll(condInfo.getMergedBlocks());
|
exitBlocks.removeAll(condInfo.getMergedBlocks());
|
||||||
|
|
||||||
if (exitBlocks.size() > 0) {
|
if (!exitBlocks.isEmpty()) {
|
||||||
BlockNode loopExit = condInfo.getElseBlock();
|
BlockNode loopExit = condInfo.getElseBlock();
|
||||||
if (loopExit != null) {
|
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()) {
|
for (Edge exitEdge : loop.getExitEdges()) {
|
||||||
if (!exitBlocks.contains(exitEdge.getSource())) {
|
if (!exitBlocks.contains(exitEdge.getSource())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tryInsertBreak(stack, loopExit, exitEdge);
|
insertBreak(stack, loopExit, exitEdge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,6 +234,7 @@ public class RegionMaker {
|
|||||||
loopRegion.setBody(body);
|
loopRegion.setBody(body);
|
||||||
}
|
}
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
insertContinue(loop);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +248,12 @@ public class RegionMaker {
|
|||||||
|| block.getInstructions().get(0).getType() != InsnType.IF) {
|
|| block.getInstructions().get(0).getType() != InsnType.IF) {
|
||||||
continue;
|
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;
|
boolean found;
|
||||||
if (block == loop.getStart() || block == loop.getEnd()
|
if (block == loop.getStart() || block == loop.getEnd()
|
||||||
|| BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
|
|| BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
|
||||||
@@ -257,6 +265,25 @@ public class RegionMaker {
|
|||||||
} else {
|
} else {
|
||||||
found = false;
|
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) {
|
if (found) {
|
||||||
return loopRegion;
|
return loopRegion;
|
||||||
}
|
}
|
||||||
@@ -266,7 +293,7 @@ public class RegionMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) {
|
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);
|
curRegion.getSubBlocks().add(loopRegion);
|
||||||
|
|
||||||
loopStart.remove(AType.LOOP);
|
loopStart.remove(AType.LOOP);
|
||||||
@@ -275,16 +302,13 @@ public class RegionMaker {
|
|||||||
BlockNode loopExit = null;
|
BlockNode loopExit = null;
|
||||||
// insert 'break' for exits
|
// insert 'break' for exits
|
||||||
List<Edge> exitEdges = loop.getExitEdges();
|
List<Edge> exitEdges = loop.getExitEdges();
|
||||||
if (exitEdges.size() == 1) {
|
for (Edge exitEdge : exitEdges) {
|
||||||
for (Edge exitEdge : exitEdges) {
|
BlockNode exit = exitEdge.getTarget();
|
||||||
BlockNode exit = exitEdge.getTarget();
|
if (insertBreak(stack, exit, exitEdge)) {
|
||||||
if (canInsertBreak(exit)) {
|
BlockNode nextBlock = getNextBlock(exit);
|
||||||
exit.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
|
if (nextBlock != null) {
|
||||||
BlockNode nextBlock = getNextBlock(exit);
|
stack.addExit(nextBlock);
|
||||||
if (nextBlock != null) {
|
loopExit = nextBlock;
|
||||||
stack.addExit(nextBlock);
|
|
||||||
loopExit = nextBlock;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,7 +331,8 @@ public class RegionMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean canInsertBreak(BlockNode exit) {
|
private boolean canInsertBreak(BlockNode exit) {
|
||||||
if (exit.contains(AFlag.RETURN)) {
|
if (exit.contains(AFlag.RETURN)
|
||||||
|
|| BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
|
List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
|
||||||
@@ -325,21 +350,136 @@ public class RegionMaker {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryInsertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
|
private boolean insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
|
||||||
BlockNode prev = null;
|
|
||||||
BlockNode exit = exitEdge.getTarget();
|
BlockNode exit = exitEdge.getTarget();
|
||||||
while (exit != null) {
|
BlockNode insertBlock = null;
|
||||||
if (prev != null && isPathExists(loopExit, exit)) {
|
boolean confirm = false;
|
||||||
// found cross
|
// process special cases
|
||||||
if (canInsertBreak(exit)) {
|
if (loopExit == exit) {
|
||||||
prev.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
|
// try/catch at loop end
|
||||||
stack.addExit(exit);
|
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>();
|
private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
|
||||||
@@ -359,8 +499,7 @@ public class RegionMaker {
|
|||||||
|
|
||||||
BlockNode body = getNextBlock(block);
|
BlockNode body = getNextBlock(block);
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
mth.add(AFlag.INCONSISTENT_CODE);
|
ErrorsCounter.methodError(mth, "Unexpected end of synchronized block");
|
||||||
LOG.warn("Unexpected end of synchronized block");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BlockNode exit;
|
BlockNode exit;
|
||||||
@@ -382,7 +521,7 @@ public class RegionMaker {
|
|||||||
* Traverse from monitor-enter thru successors and collect blocks contains monitor-exit
|
* Traverse from monitor-enter thru successors and collect blocks contains monitor-exit
|
||||||
*/
|
*/
|
||||||
private static void traverseMonitorExits(SynchronizedRegion region, InsnArg arg, BlockNode block,
|
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);
|
visited.add(block);
|
||||||
for (InsnNode insn : block.getInstructions()) {
|
for (InsnNode insn : block.getInstructions()) {
|
||||||
if (insn.getType() == InsnType.MONITOR_EXIT
|
if (insn.getType() == InsnType.MONITOR_EXIT
|
||||||
@@ -440,9 +579,13 @@ public class RegionMaker {
|
|||||||
// invert simple condition (compiler often do it)
|
// invert simple condition (compiler often do it)
|
||||||
currentIf = IfInfo.invert(currentIf);
|
currentIf = IfInfo.invert(currentIf);
|
||||||
}
|
}
|
||||||
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
IfInfo modifiedIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
||||||
if (currentIf == null) {
|
if (modifiedIf != null) {
|
||||||
// invalid merged if, check simple one again
|
currentIf = modifiedIf;
|
||||||
|
} else {
|
||||||
|
if (currentIf.getMergedBlocks().size() <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
currentIf = makeIfInfo(block);
|
currentIf = makeIfInfo(block);
|
||||||
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
|
||||||
if (currentIf == null) {
|
if (currentIf == null) {
|
||||||
@@ -450,6 +593,7 @@ public class RegionMaker {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
confirmMerge(currentIf);
|
||||||
|
|
||||||
IfRegion ifRegion = new IfRegion(currentRegion, block);
|
IfRegion ifRegion = new IfRegion(currentRegion, block);
|
||||||
ifRegion.setCondition(currentIf.getCondition());
|
ifRegion.setCondition(currentIf.getCondition());
|
||||||
@@ -489,7 +633,7 @@ public class RegionMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len);
|
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());
|
BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors());
|
||||||
assert c != null;
|
assert c != null;
|
||||||
blocksMap.put(c, entry.getValue());
|
blocksMap.put(c, entry.getValue());
|
||||||
@@ -626,15 +770,6 @@ public class RegionMaker {
|
|||||||
handler.getHandlerRegion().addAttr(excHandlerAttr);
|
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) {
|
static boolean isEqualPaths(BlockNode b1, BlockNode b2) {
|
||||||
if (b1 == b2) {
|
if (b1 == b2) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import jadx.core.dex.nodes.IContainer;
|
|||||||
import jadx.core.dex.nodes.IRegion;
|
import jadx.core.dex.nodes.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.regions.LoopRegion;
|
|
||||||
import jadx.core.dex.regions.Region;
|
import jadx.core.dex.regions.Region;
|
||||||
import jadx.core.dex.regions.SynchronizedRegion;
|
import jadx.core.dex.regions.SynchronizedRegion;
|
||||||
|
import jadx.core.dex.regions.loops.LoopRegion;
|
||||||
import jadx.core.dex.visitors.AbstractVisitor;
|
import jadx.core.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.InstructionRemover;
|
import jadx.core.utils.InstructionRemover;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
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.IRegion;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
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.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.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
@@ -72,7 +73,8 @@ public class ReturnVisitor extends AbstractVisitor {
|
|||||||
for (IRegion region : regionStack) {
|
for (IRegion region : regionStack) {
|
||||||
// ignore paths on other branches
|
// ignore paths on other branches
|
||||||
if (region instanceof IfRegion
|
if (region instanceof IfRegion
|
||||||
|| region instanceof SwitchRegion) {
|
|| region instanceof SwitchRegion
|
||||||
|
|| region instanceof TryCatchRegion) {
|
||||||
curContainer = region;
|
curContainer = region;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,56 +2,84 @@ package jadx.core.dex.visitors.regions;
|
|||||||
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
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.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.regions.IfRegion;
|
|
||||||
import jadx.core.dex.regions.Region;
|
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.dex.visitors.CodeShrinker;
|
||||||
import jadx.core.utils.InsnList;
|
import jadx.core.utils.InsnList;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class TernaryMod {
|
public class TernaryMod {
|
||||||
|
|
||||||
private TernaryMod() {
|
private TernaryMod() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void makeTernaryInsn(MethodNode mth, IfRegion ifRegion) {
|
static boolean makeTernaryInsn(MethodNode mth, IfRegion ifRegion) {
|
||||||
if (ifRegion.contains(AFlag.ELSE_IF_CHAIN)) {
|
if (ifRegion.contains(AFlag.ELSE_IF_CHAIN)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
IContainer thenRegion = ifRegion.getThenRegion();
|
IContainer thenRegion = ifRegion.getThenRegion();
|
||||||
IContainer elseRegion = ifRegion.getElseRegion();
|
IContainer elseRegion = ifRegion.getElseRegion();
|
||||||
if (thenRegion == null || elseRegion == null) {
|
if (thenRegion == null || elseRegion == null) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
BlockNode tb = getTernaryInsnBlock(thenRegion);
|
BlockNode tb = getTernaryInsnBlock(thenRegion);
|
||||||
BlockNode eb = getTernaryInsnBlock(elseRegion);
|
BlockNode eb = getTernaryInsnBlock(elseRegion);
|
||||||
if (tb == null || eb == null) {
|
if (tb == null || eb == null) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
BlockNode header = ifRegion.getHeader();
|
BlockNode header = ifRegion.getHeader();
|
||||||
InsnNode t = tb.getInstructions().get(0);
|
InsnNode t = tb.getInstructions().get(0);
|
||||||
InsnNode e = eb.getInstructions().get(0);
|
InsnNode e = eb.getInstructions().get(0);
|
||||||
|
|
||||||
if (t.getResult() != null && e.getResult() != null
|
if (t.getSourceLine() != e.getSourceLine()) {
|
||||||
&& t.getResult().equalRegisterAndType(e.getResult())
|
if (t.getSourceLine() != 0 && e.getSourceLine() != 0) {
|
||||||
&& t.getResult().getSVar().isUsedInPhi()) {
|
// 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(tb, t);
|
||||||
InsnList.remove(eb, e);
|
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(),
|
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
|
||||||
resArg, InsnArg.wrapArg(t), InsnArg.wrapArg(e));
|
resArg, InsnArg.wrapArg(t), InsnArg.wrapArg(e));
|
||||||
ternInsn.setSourceLine(t.getSourceLine());
|
ternInsn.setSourceLine(t.getSourceLine());
|
||||||
TernaryRegion tern = new TernaryRegion(ifRegion, header);
|
|
||||||
// TODO: add api for replace regions
|
|
||||||
ifRegion.setTernRegion(tern);
|
|
||||||
|
|
||||||
// remove 'if' instruction
|
// remove 'if' instruction
|
||||||
header.getInstructions().clear();
|
header.getInstructions().clear();
|
||||||
@@ -59,11 +87,15 @@ public class TernaryMod {
|
|||||||
|
|
||||||
// shrink method again
|
// shrink method again
|
||||||
CodeShrinker.shrinkMethod(mth);
|
CodeShrinker.shrinkMethod(mth);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mth.getReturnType().equals(ArgType.VOID)
|
if (!mth.getReturnType().equals(ArgType.VOID)
|
||||||
&& t.getType() == InsnType.RETURN && e.getType() == InsnType.RETURN) {
|
&& t.getType() == InsnType.RETURN && e.getType() == InsnType.RETURN) {
|
||||||
|
|
||||||
|
if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
InsnList.remove(tb, t);
|
InsnList.remove(tb, t);
|
||||||
InsnList.remove(eb, e);
|
InsnList.remove(eb, e);
|
||||||
tb.remove(AFlag.RETURN);
|
tb.remove(AFlag.RETURN);
|
||||||
@@ -78,10 +110,10 @@ public class TernaryMod {
|
|||||||
header.getInstructions().add(retInsn);
|
header.getInstructions().add(retInsn);
|
||||||
header.add(AFlag.RETURN);
|
header.add(AFlag.RETURN);
|
||||||
|
|
||||||
ifRegion.setTernRegion(new TernaryRegion(ifRegion, header));
|
|
||||||
|
|
||||||
CodeShrinker.shrinkMethod(mth);
|
CodeShrinker.shrinkMethod(mth);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlockNode getTernaryInsnBlock(IContainer thenRegion) {
|
private static BlockNode getTernaryInsnBlock(IContainer thenRegion) {
|
||||||
@@ -99,4 +131,59 @@ public class TernaryMod {
|
|||||||
}
|
}
|
||||||
return null;
|
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.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class EliminatePhiNodes extends AbstractVisitor {
|
public class EliminatePhiNodes extends AbstractVisitor {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(EliminatePhiNodes.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodNode mth) throws JadxException {
|
public void visit(MethodNode mth) throws JadxException {
|
||||||
if (mth.isNoCode()) {
|
if (mth.isNoCode()) {
|
||||||
@@ -29,13 +34,20 @@ public class EliminatePhiNodes extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
List<PhiInsn> list = phiList.getList();
|
List<PhiInsn> list = phiList.getList();
|
||||||
for (PhiInsn phiInsn : list) {
|
for (PhiInsn phiInsn : list) {
|
||||||
for (Iterator<InsnNode> iterator = block.getInstructions().iterator(); iterator.hasNext(); ) {
|
removeInsn(mth, block, phiInsn);
|
||||||
InsnNode insn = iterator.next();
|
|
||||||
if (insn == phiInsn) {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
package jadx.core.dex.visitors.ssa;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
@@ -40,7 +41,10 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
placePhi(mth, i, la);
|
placePhi(mth, i, la);
|
||||||
}
|
}
|
||||||
renameVariables(mth);
|
renameVariables(mth);
|
||||||
removeUselessPhi(mth);
|
fixLastTryCatchAssign(mth);
|
||||||
|
if (removeUselessPhi(mth)) {
|
||||||
|
renameVariables(mth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
|
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());
|
PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size());
|
||||||
phiList.getList().add(phiInsn);
|
phiList.getList().add(phiInsn);
|
||||||
|
phiInsn.setOffset(block.getStartOffset());
|
||||||
block.getInstructions().add(0, phiInsn);
|
block.getInstructions().add(0, phiInsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +131,9 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
|
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
|
||||||
}
|
}
|
||||||
for (PhiInsn phiInsn : phiList.getList()) {
|
for (PhiInsn phiInsn : phiList.getList()) {
|
||||||
|
if (j >= phiInsn.getArgsCount()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int regNum = phiInsn.getResult().getRegNum();
|
int regNum = phiInsn.getResult().getRegNum();
|
||||||
SSAVar var = vars[regNum];
|
SSAVar var = vars[regNum];
|
||||||
if (var == null) {
|
if (var == null) {
|
||||||
@@ -143,7 +151,27 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
System.arraycopy(inputVars, 0, vars, 0, vars.length);
|
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>();
|
List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
|
||||||
for (SSAVar var : mth.getSVars()) {
|
for (SSAVar var : mth.getSVars()) {
|
||||||
// phi result not used
|
// phi result not used
|
||||||
@@ -163,7 +191,7 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
removePhiWithSameArgs(phi, insnToRemove);
|
removePhiWithSameArgs(phi, insnToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removePhiList(mth, insnToRemove);
|
return removePhiList(mth, insnToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removePhiWithSameArgs(PhiInsn phi, List<PhiInsn> 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()) {
|
if (insnToRemove.isEmpty()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||||
@@ -213,5 +241,6 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
insnToRemove.clear();
|
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.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.IndexInsnNode;
|
import jadx.core.dex.instructions.IndexInsnNode;
|
||||||
import jadx.core.dex.instructions.InvokeNode;
|
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.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
@@ -15,6 +16,9 @@ import java.util.List;
|
|||||||
|
|
||||||
public class PostTypeInference {
|
public class PostTypeInference {
|
||||||
|
|
||||||
|
private PostTypeInference() {
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean process(MethodNode mth, InsnNode insn) {
|
public static boolean process(MethodNode mth, InsnNode insn) {
|
||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case CONST:
|
case CONST:
|
||||||
@@ -24,7 +28,7 @@ public class PostTypeInference {
|
|||||||
long lit = litArg.getLiteral();
|
long lit = litArg.getLiteral();
|
||||||
if (lit != 0) {
|
if (lit != 0) {
|
||||||
// incorrect literal value for object
|
// 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
|
// can't merge with object -> force it
|
||||||
litArg.setType(type);
|
litArg.setType(type);
|
||||||
res.getSVar().setType(type);
|
res.getSVar().setType(type);
|
||||||
@@ -84,29 +88,36 @@ public class PostTypeInference {
|
|||||||
|
|
||||||
case CHECK_CAST: {
|
case CHECK_CAST: {
|
||||||
ArgType castType = (ArgType) ((IndexInsnNode) insn).getIndex();
|
ArgType castType = (ArgType) ((IndexInsnNode) insn).getIndex();
|
||||||
SSAVar sVar = insn.getResult().getSVar();
|
RegisterArg result = insn.getResult();
|
||||||
// don't override generic types of same base class
|
// 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) {
|
if (!skip) {
|
||||||
// workaround for compiler bug (see TestDuplicateCast)
|
// workaround for compiler bug (see TestDuplicateCast)
|
||||||
sVar.setType(castType);
|
result.getSVar().setType(castType);
|
||||||
}
|
}
|
||||||
return true;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
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) {
|
private static boolean fixArrayTypes(InsnArg array, InsnArg elem) {
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import jadx.core.dex.nodes.InsnNode;
|
|||||||
|
|
||||||
public class SelectTypeVisitor {
|
public class SelectTypeVisitor {
|
||||||
|
|
||||||
|
private SelectTypeVisitor() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void visit(InsnNode insn) {
|
public static void visit(InsnNode insn) {
|
||||||
InsnArg res = insn.getResult();
|
InsnArg res = insn.getResult();
|
||||||
if (res != null && !res.getType().isTypeKnown()) {
|
if (res != null && !res.getType().isTypeKnown()) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package jadx.core.dex.visitors.typeinference;
|
package jadx.core.dex.visitors.typeinference;
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
|
||||||
import jadx.core.dex.instructions.PhiInsn;
|
import jadx.core.dex.instructions.PhiInsn;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
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.nodes.MethodNode;
|
||||||
import jadx.core.dex.visitors.AbstractVisitor;
|
import jadx.core.dex.visitors.AbstractVisitor;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -30,45 +28,36 @@ public class TypeInference extends AbstractVisitor {
|
|||||||
|
|
||||||
// search variable name
|
// search variable name
|
||||||
String name = processVarName(var);
|
String name = processVarName(var);
|
||||||
if (name != null) {
|
var.setName(name);
|
||||||
var.setName(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix type for vars used only in Phi nodes
|
// fix type for vars used only in Phi nodes
|
||||||
for (SSAVar sVar : mth.getSVars()) {
|
for (SSAVar sVar : mth.getSVars()) {
|
||||||
if (sVar.isUsedInPhi()) {
|
PhiInsn phi = sVar.getUsedInPhi();
|
||||||
processPhiNode(sVar.getUsedInPhi());
|
if (phi != null) {
|
||||||
|
processPhiNode(phi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArgType processType(SSAVar var) {
|
private static ArgType processType(SSAVar var) {
|
||||||
RegisterArg assign = var.getAssign();
|
RegisterArg assign = var.getAssign();
|
||||||
List<RegisterArg> useList = var.getUseList();
|
List<RegisterArg> useList = var.getUseList();
|
||||||
if (assign != null
|
if (useList.isEmpty() || var.isTypeImmutable()) {
|
||||||
&& (useList.isEmpty() || assign.isTypeImmutable())) {
|
|
||||||
return assign.getType();
|
return assign.getType();
|
||||||
}
|
}
|
||||||
ArgType type;
|
ArgType type = assign.getType();
|
||||||
if (assign != null) {
|
|
||||||
type = assign.getType();
|
|
||||||
} else {
|
|
||||||
type = ArgType.UNKNOWN;
|
|
||||||
}
|
|
||||||
for (RegisterArg arg : useList) {
|
for (RegisterArg arg : useList) {
|
||||||
ArgType useType = arg.getType();
|
ArgType useType = arg.getType();
|
||||||
if (useType.isTypeKnown()) {
|
ArgType newType = ArgType.merge(type, useType);
|
||||||
type = ArgType.merge(type, useType);
|
if (newType != null) {
|
||||||
}
|
type = newType;
|
||||||
if (arg.getParentInsn().contains(AFlag.INCONSISTENT_CODE)) {
|
|
||||||
throw new JadxRuntimeException("not removed arg");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPhiNode(PhiInsn phi) {
|
private static void processPhiNode(PhiInsn phi) {
|
||||||
ArgType type = phi.getResult().getType();
|
ArgType type = phi.getResult().getType();
|
||||||
if (!type.isTypeKnown()) {
|
if (!type.isTypeKnown()) {
|
||||||
for (InsnArg arg : phi.getArguments()) {
|
for (InsnArg arg : phi.getArguments()) {
|
||||||
@@ -86,20 +75,17 @@ public class TypeInference extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String processVarName(SSAVar var) {
|
private static String processVarName(SSAVar var) {
|
||||||
String name = null;
|
String name = var.getAssign().getName();
|
||||||
if (var.getAssign() != null) {
|
|
||||||
name = var.getAssign().getName();
|
|
||||||
}
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
for (RegisterArg arg : var.getUseList()) {
|
for (RegisterArg arg : var.getUseList()) {
|
||||||
String vName = arg.getName();
|
String vName = arg.getName();
|
||||||
if (vName != null) {
|
if (vName != null) {
|
||||||
name = vName;
|
return vName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return name;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
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.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.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.dex.regions.conditions.IfCondition;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -113,7 +121,12 @@ public class BlockUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) {
|
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()) {
|
for (BlockNode bn : mth.getBasicBlocks()) {
|
||||||
if (blockContains(bn, insn)) {
|
if (blockContains(bn, insn)) {
|
||||||
return bn;
|
return bn;
|
||||||
@@ -122,6 +135,87 @@ public class BlockUtils {
|
|||||||
return null;
|
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) {
|
public static BitSet blocksToBitSet(MethodNode mth, List<BlockNode> blocks) {
|
||||||
BitSet bs = new BitSet(mth.getBasicBlocks().size());
|
BitSet bs = new BitSet(mth.getBasicBlocks().size());
|
||||||
for (BlockNode block : blocks) {
|
for (BlockNode block : blocks) {
|
||||||
@@ -171,16 +265,16 @@ public class BlockUtils {
|
|||||||
Set<BlockNode> set = new HashSet<BlockNode>();
|
Set<BlockNode> set = new HashSet<BlockNode>();
|
||||||
set.add(start);
|
set.add(start);
|
||||||
if (start != end) {
|
if (start != end) {
|
||||||
addPredcessors(set, end, start);
|
addPredecessors(set, end, start);
|
||||||
}
|
}
|
||||||
return set;
|
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);
|
set.add(from);
|
||||||
for (BlockNode pred : from.getPredecessors()) {
|
for (BlockNode pred : from.getPredecessors()) {
|
||||||
if (pred != until && !set.contains(pred)) {
|
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());
|
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) {
|
public static boolean isOnlyOnePathExists(BlockNode start, BlockNode end) {
|
||||||
if (start == end) {
|
if (start == end) {
|
||||||
return true;
|
return true;
|
||||||
@@ -345,4 +416,14 @@ public class BlockUtils {
|
|||||||
}
|
}
|
||||||
return block == end;
|
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;
|
import java.util.BitSet;
|
||||||
|
|
||||||
public class EmptyBitSet extends BitSet {
|
public final class EmptyBitSet extends BitSet {
|
||||||
|
|
||||||
private static final long serialVersionUID = -1194884945157778639L;
|
private static final long serialVersionUID = -1194884945157778639L;
|
||||||
|
|
||||||
|
public static final BitSet EMPTY = new EmptyBitSet();
|
||||||
|
|
||||||
public EmptyBitSet() {
|
public EmptyBitSet() {
|
||||||
super(0);
|
super(0);
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,7 @@ public class EmptyBitSet extends BitSet {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BitSet get(int fromIndex, int toIndex) {
|
public BitSet get(int fromIndex, int toIndex) {
|
||||||
throw new UnsupportedOperationException();
|
return EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,4 +86,9 @@ public class EmptyBitSet extends BitSet {
|
|||||||
public void andNot(BitSet set) {
|
public void andNot(BitSet set) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class ErrorsCounter {
|
|||||||
if (e.getClass() == JadxOverflowException.class) {
|
if (e.getClass() == JadxOverflowException.class) {
|
||||||
// don't print full stack trace
|
// don't print full stack trace
|
||||||
e = new JadxOverflowException(e.getMessage());
|
e = new JadxOverflowException(e.getMessage());
|
||||||
LOG.error(msg);
|
LOG.error(msg + ", message: " + e.getMessage());
|
||||||
} else {
|
} else {
|
||||||
LOG.error(msg, e);
|
LOG.error(msg, e);
|
||||||
}
|
}
|
||||||
@@ -57,6 +57,10 @@ public class ErrorsCounter {
|
|||||||
return msg;
|
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) {
|
public static String methodError(MethodNode mth, String errorMsg, Throwable e) {
|
||||||
String msg = formatErrorMsg(mth, errorMsg);
|
String msg = formatErrorMsg(mth, errorMsg);
|
||||||
mth.dex().root().getErrorsCounter().addError(mth, msg, e);
|
mth.dex().root().getErrorsCounter().addError(mth, msg, e);
|
||||||
@@ -69,7 +73,7 @@ public class ErrorsCounter {
|
|||||||
|
|
||||||
public void printReport() {
|
public void printReport() {
|
||||||
if (getErrorCount() > 0) {
|
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);
|
List<Object> nodes = new ArrayList<Object>(errorNodes);
|
||||||
Collections.sort(nodes, new Comparator<Object>() {
|
Collections.sort(nodes, new Comparator<Object>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -79,7 +83,7 @@ public class ErrorsCounter {
|
|||||||
});
|
});
|
||||||
for (Object node : nodes) {
|
for (Object node : nodes) {
|
||||||
String nodeName = node.getClass().getSimpleName().replace("Node", "");
|
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) {
|
public static String insnTypeToString(InsnType type) {
|
||||||
return insnTypeToString(type.toString());
|
return type.toString() + " ";
|
||||||
}
|
|
||||||
|
|
||||||
public static String insnTypeToString(String str) {
|
|
||||||
return String.format("%s ", str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String indexToString(Object index) {
|
public static String indexToString(Object index) {
|
||||||
@@ -49,7 +45,7 @@ public class InsnUtils {
|
|||||||
if (index instanceof String) {
|
if (index instanceof String) {
|
||||||
return "\"" + index + "\"";
|
return "\"" + index + "\"";
|
||||||
} else {
|
} else {
|
||||||
return " " + index;
|
return index.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
import jadx.core.Consts;
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
@@ -66,9 +65,6 @@ public class InstructionRemover {
|
|||||||
public static void unbindInsn(MethodNode mth, InsnNode insn) {
|
public static void unbindInsn(MethodNode mth, InsnNode insn) {
|
||||||
RegisterArg r = insn.getResult();
|
RegisterArg r = insn.getResult();
|
||||||
if (r != null && r.getSVar() != null) {
|
if (r != null && r.getSVar() != null) {
|
||||||
if (Consts.DEBUG && r.getSVar().getUseCount() != 0) {
|
|
||||||
LOG.debug("Unbind insn with result: {}", insn);
|
|
||||||
}
|
|
||||||
mth.removeSVar(r.getSVar());
|
mth.removeSVar(r.getSVar());
|
||||||
}
|
}
|
||||||
for (InsnArg arg : insn.getArguments()) {
|
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.BlockNode;
|
||||||
import jadx.core.dex.nodes.IContainer;
|
import jadx.core.dex.nodes.IContainer;
|
||||||
import jadx.core.dex.nodes.IRegion;
|
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.CatchAttr;
|
||||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||||
@@ -24,7 +27,7 @@ public class RegionUtils {
|
|||||||
public static boolean hasExitEdge(IContainer container) {
|
public static boolean hasExitEdge(IContainer container) {
|
||||||
if (container instanceof BlockNode) {
|
if (container instanceof BlockNode) {
|
||||||
BlockNode block = (BlockNode) container;
|
BlockNode block = (BlockNode) container;
|
||||||
return block.getSuccessors().size() != 0
|
return !block.getSuccessors().isEmpty()
|
||||||
&& !block.contains(AFlag.RETURN);
|
&& !block.contains(AFlag.RETURN);
|
||||||
} else if (container instanceof IRegion) {
|
} else if (container instanceof IRegion) {
|
||||||
IRegion region = (IRegion) container;
|
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
|
* Return true if last block in region has no successors
|
||||||
*/
|
*/
|
||||||
@@ -83,7 +109,7 @@ public class RegionUtils {
|
|||||||
|
|
||||||
public static boolean notEmpty(IContainer container) {
|
public static boolean notEmpty(IContainer container) {
|
||||||
if (container instanceof BlockNode) {
|
if (container instanceof BlockNode) {
|
||||||
return ((BlockNode) container).getInstructions().size() != 0;
|
return !((BlockNode) container).getInstructions().isEmpty();
|
||||||
} else if (container instanceof IRegion) {
|
} else if (container instanceof IRegion) {
|
||||||
IRegion region = (IRegion) container;
|
IRegion region = (IRegion) container;
|
||||||
for (IContainer block : region.getSubBlocks()) {
|
for (IContainer block : region.getSubBlocks()) {
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -94,14 +91,7 @@ public class Utils {
|
|||||||
return sw.getBuffer().toString();
|
return sw.getBuffer().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void makeDirsForFile(File file) {
|
public static int compare(int x, int y) {
|
||||||
File dir = file.getParentFile();
|
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package jadx.core.utils.files;
|
package jadx.core.utils.files;
|
||||||
|
|
||||||
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
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) {
|
if (ba.length == 0) {
|
||||||
throw new JadxException(j2d.isError() ? j2d.getDxErrors() : "Empty dx output");
|
throw new JadxException(j2d.isError() ? j2d.getDxErrors() : "Empty dx output");
|
||||||
} else if (j2d.isError()) {
|
} else if (j2d.isError()) {
|
||||||
LOG.warn("dx message: " + j2d.getDxErrors());
|
LOG.warn("dx message: {}", j2d.getDxErrors());
|
||||||
}
|
}
|
||||||
return new Dex(ba);
|
return new Dex(ba);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
package jadx.tests
|
package jadx.tests
|
||||||
|
|
||||||
import jadx.api.IJadxArgs
|
import jadx.api.IJadxArgs
|
||||||
import jadx.api.JadxDecompiler
|
import jadx.api.JadxDecompiler
|
||||||
import jadx.core.utils.exceptions.JadxException
|
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