Compare commits

...

64 Commits

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

Some files were not shown because too many files have changed in this diff Show More