Commit cb6ff606 authored by Skylot's avatar Skylot

Fix small issues and improve code

parent 26800fb7
......@@ -18,8 +18,6 @@ public interface IJadxArgs {
boolean isFallbackMode();
boolean isNotObfuscated();
boolean isVerbose();
boolean isPrintHelp();
......
......@@ -33,9 +33,6 @@ public class JadxArgs implements IJadxArgs {
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)", help = true)
protected boolean fallbackMode = false;
@Parameter(names = {"--not-obfuscated"}, description = "set this flag if code not obfuscated")
protected boolean notObfuscated = false;
@Parameter(names = {"--cfg"}, description = "save methods control flow graph")
protected boolean cfgOutput = false;
......@@ -178,11 +175,6 @@ public class JadxArgs implements IJadxArgs {
}
@Override
public boolean isNotObfuscated() {
return notObfuscated;
}
@Override
public boolean isVerbose() {
return verbose;
}
......
......@@ -82,6 +82,9 @@ public class ClassGen {
if (cls.getAttributes().contains(AttributeFlag.DONT_GENERATE))
return;
if (cls.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE))
code.startLine("// jadx: inconsistent code");
makeClassDeclaration(code);
makeClassBody(code);
code.endl();
......@@ -124,7 +127,7 @@ public class ClassGen {
else
clsCode.add("implements ");
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext();) {
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext(); ) {
ClassInfo interf = it.next();
clsCode.add(useClass(interf));
if (it.hasNext())
......@@ -150,7 +153,7 @@ public class ClassGen {
code.add(useClass(type));
if (list != null && !list.isEmpty()) {
code.add(" extends ");
for (Iterator<ArgType> it = list.iterator(); it.hasNext();) {
for (Iterator<ArgType> it = list.iterator(); it.hasNext(); ) {
ArgType g = it.next();
code.add(useClass(g));
if (it.hasNext()) {
......@@ -167,11 +170,16 @@ public class ClassGen {
public void makeClassBody(CodeWriter clsCode) throws CodegenException {
clsCode.add('{');
CodeWriter mthsCode = makeMethods(clsCode, cls.getMethods());
clsCode.add(makeFields(clsCode, cls, cls.getFields()));
CodeWriter fieldsCode = makeFields(clsCode, cls, cls.getFields());
clsCode.add(fieldsCode);
if (fieldsCode.notEmpty() && mthsCode.notEmpty())
clsCode.endl();
// insert inner classes code
if (cls.getInnerClasses().size() != 0) {
clsCode.add(makeInnerClasses(cls, clsCode.getIndent()));
if (mthsCode.notEmpty())
clsCode.endl();
}
clsCode.add(mthsCode);
clsCode.startLine('}');
......@@ -192,7 +200,7 @@ public class ClassGen {
private CodeWriter makeMethods(CodeWriter clsCode, List<MethodNode> mthList) {
CodeWriter code = new CodeWriter(clsCode.getIndent() + 1);
for (Iterator<MethodNode> it = mthList.iterator(); it.hasNext();) {
for (Iterator<MethodNode> it = mthList.iterator(); it.hasNext(); ) {
MethodNode mth = it.next();
if (mth.getAttributes().contains(AttributeFlag.DONT_GENERATE))
continue;
......@@ -235,16 +243,19 @@ public class ClassGen {
EnumClassAttr enumFields = (EnumClassAttr) cls.getAttributes().get(AttributeType.ENUM_CLASS);
if (enumFields != null) {
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
InsnGen igen = new InsnGen(mthGen, enumFields.getStaticMethod(), false);
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext();) {
InsnGen igen = null;
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
EnumField f = it.next();
code.startLine(f.getName());
if (f.getArgs().size() != 0) {
code.add('(');
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext();) {
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
InsnArg arg = aIt.next();
if (igen == null) {
// don't init mth gen if this is simple enum
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
igen = new InsnGen(mthGen, enumFields.getStaticMethod(), false);
}
code.add(igen.arg(arg));
if (aIt.hasNext())
code.add(", ");
......@@ -281,8 +292,6 @@ public class ClassGen {
}
code.add(';');
}
if (fields.size() != 0)
code.endl();
return code;
}
......@@ -290,11 +299,11 @@ public class ClassGen {
if (clsType.isGenericType()) {
return clsType.getObject();
}
return useClass(ClassInfo.fromType(cls.dex(), clsType));
return useClass(ClassInfo.fromType(clsType));
}
public String useClass(ClassInfo classInfo) {
String baseClass = useClassInner(classInfo);
String baseClass = useClassInternal(classInfo);
ArgType[] generics = classInfo.getType().getGenericTypes();
if (generics != null) {
StringBuilder sb = new StringBuilder();
......@@ -318,9 +327,9 @@ public class ClassGen {
}
}
private String useClassInner(ClassInfo classInfo) {
private String useClassInternal(ClassInfo classInfo) {
if (parentGen != null)
return parentGen.useClassInner(classInfo);
return parentGen.useClassInternal(classInfo);
String clsStr = classInfo.getFullName();
if (fallback)
......
......@@ -133,6 +133,10 @@ public class CodeWriter {
}
}
public boolean notEmpty() {
return buf.length() != 0;
}
@Override
public String toString() {
return buf.toString();
......@@ -183,5 +187,4 @@ public class CodeWriter {
throw new JadxRuntimeException("Can't create directory " + dir);
}
}
}
......@@ -13,13 +13,6 @@ public final class ClassInfo {
private static final Map<ArgType, ClassInfo> CLASSINFO_CACHE = new WeakHashMap<ArgType, ClassInfo>();
private static final String DEFAULT_PACKAGE_NAME = "defpackage";
private final String clsName;
private final String clsPackage;
private final ArgType type;
private final String fullName;
private final ClassInfo parentClass; // not equals null if this is inner class
public static ClassInfo fromDex(DexNode dex, int clsIndex) {
if (clsIndex == DexNode.NO_INDEX)
return null;
......@@ -28,17 +21,17 @@ public final class ClassInfo {
if (type.isArray())
type = ArgType.OBJECT;
return fromType(dex, type);
return fromType(type);
}
public static ClassInfo fromName(DexNode dex, String clsName) {
return fromType(dex, ArgType.object(clsName));
public static ClassInfo fromName(String clsName) {
return fromType(ArgType.object(clsName));
}
public static ClassInfo fromType(DexNode dex, ArgType type) {
public static ClassInfo fromType(ArgType type) {
ClassInfo cls = CLASSINFO_CACHE.get(type);
if (cls == null) {
cls = new ClassInfo(dex, type);
cls = new ClassInfo(type);
CLASSINFO_CACHE.put(type, cls);
}
return cls;
......@@ -48,21 +41,28 @@ public final class ClassInfo {
CLASSINFO_CACHE.clear();
}
private ClassInfo(DexNode dex, ArgType type) {
private final ArgType type;
private String pkg;
private String name;
private String fullName;
private ClassInfo parentClass; // not equals null if this is inner class
private ClassInfo(ArgType type) {
assert type.isObject() : "Not class type: " + type;
this.type = type;
splitNames(true);
}
private void splitNames(boolean canBeInner) {
String fullObjectName = type.getObject();
assert fullObjectName.indexOf('/') == -1 : "Raw type: " + type;
boolean notObfuscated = dex.root().getJadxArgs().isNotObfuscated();
String name;
String pkg;
int dot = fullObjectName.lastIndexOf('.');
if (dot == -1) {
// rename default package if it used from class with package (often for obfuscated apps),
pkg = (notObfuscated ? "" : DEFAULT_PACKAGE_NAME);
pkg = DEFAULT_PACKAGE_NAME;
name = fullObjectName;
} else {
pkg = fullObjectName.substring(0, dot);
......@@ -70,27 +70,30 @@ public final class ClassInfo {
}
int sep = name.lastIndexOf('$');
if (sep > 0 && sep != name.length() - 1) {
if (canBeInner && sep > 0 && sep != name.length() - 1) {
String parClsName = pkg + '.' + name.substring(0, sep);
parentClass = fromName(dex, parClsName);
parentClass = fromName(parClsName);
name = name.substring(sep + 1);
} else {
parentClass = null;
}
if (Character.isDigit(name.charAt(0)))
char firstChar = name.charAt(0);
if (Character.isDigit(firstChar)) {
name = "InnerClass_" + name;
if (NameMapper.isReserved(name))
} else if(firstChar == '$') {
name = "_" + name;
}
if (NameMapper.isReserved(name)) {
name += "_";
}
this.fullName = (parentClass != null ? parentClass.getFullName() : pkg) + "." + name;
this.clsName = name;
this.clsPackage = pkg;
this.name = name;
}
public String getFullPath() {
return clsPackage.replace('.', File.separatorChar) + File.separatorChar
return pkg.replace('.', File.separatorChar)
+ File.separatorChar
+ getNameWithoutPackage().replace('.', '_');
}
......@@ -99,19 +102,19 @@ public final class ClassInfo {
}
public String getShortName() {
return clsName;
return name;
}
public String getPackage() {
return clsPackage;
return pkg;
}
public boolean isPackageDefault() {
return clsPackage.isEmpty() || clsPackage.equals(DEFAULT_PACKAGE_NAME);
return pkg.isEmpty() || pkg.equals(DEFAULT_PACKAGE_NAME);
}
public String getNameWithoutPackage() {
return (parentClass != null ? parentClass.getNameWithoutPackage() + "." : "") + clsName;
return (parentClass != null ? parentClass.getNameWithoutPackage() + "." : "") + name;
}
public ClassInfo getParentClass() {
......@@ -122,6 +125,10 @@ public final class ClassInfo {
return parentClass != null;
}
public void notInner() {
splitNames(false);
}
public ArgType getType() {
return type;
}
......@@ -133,7 +140,7 @@ public final class ClassInfo {
@Override
public int hashCode() {
return this.getFullName().hashCode();
return type.hashCode();
}
@Override
......
......@@ -77,6 +77,10 @@ public class TypedVar {
@Override
public String toString() {
return (name != null ? "'" + name + "' " : "") + type.toString();
StringBuilder sb = new StringBuilder();
if(name != null)
sb.append('\'').append(name).append("' ");
sb.append(type);
return sb.toString();
}
}
......@@ -19,6 +19,7 @@ public class BlockNode extends AttrNode implements IBlock {
private List<BlockNode> predecessors = new ArrayList<BlockNode>(1);
private List<BlockNode> successors = new ArrayList<BlockNode>(1);
private List<BlockNode> cleanSuccessors;
private BitSet doms; // all dominators
private BlockNode idom; // immediate dominator
......@@ -48,8 +49,6 @@ public class BlockNode extends AttrNode implements IBlock {
return successors;
}
private List<BlockNode> cleanSuccessors;
public List<BlockNode> getCleanSuccessors() {
return cleanSuccessors;
}
......
......@@ -154,10 +154,10 @@ public class ClassNode extends AttrNode implements ILoadable {
if (list != null && !list.isEmpty()) {
try {
ArgType st = list.remove(0);
this.superClass = ClassInfo.fromType(dex, st);
this.superClass = ClassInfo.fromType(st);
int i = 0;
for (ArgType it : list) {
ClassInfo interf = ClassInfo.fromType(dex, it);
ClassInfo interf = ClassInfo.fromType(it);
interfaces.set(i, interf);
i++;
}
......
......@@ -56,16 +56,16 @@ public class RootNode {
if (cls.getClassInfo().isInner())
inner.add(cls);
}
getClasses().removeAll(inner);
for (ClassNode cls : inner) {
ClassNode parent = resolveClass(cls.getClassInfo().getParentClass());
if (parent == null)
LOG.warn("Can't add inner class: {} to {}", cls, cls.getClassInfo().getParentClass());
else
if (parent == null) {
cls.getClassInfo().notInner();
} else {
parent.addInnerClass(cls);
getClasses().remove(cls);
}
}
inner.clear();
}
public List<ClassNode> getClasses() {
......
......@@ -159,7 +159,6 @@ public class BlockMakerVisitor extends AbstractVisitor {
BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap);
// skip self loop in handler
if (connBlock != destBlock)
// && !connBlock.getPredecessors().contains(destBlock))
connect(connBlock, destBlock);
}
}
......@@ -208,9 +207,10 @@ public class BlockMakerVisitor extends AbstractVisitor {
}
private static void computeDominators(MethodNode mth) {
int nBlocks = mth.getBasicBlocks().size();
List<BlockNode> basicBlocks = mth.getBasicBlocks();
int nBlocks = basicBlocks.size();
for (int i = 0; i < nBlocks; i++) {
BlockNode block = mth.getBasicBlocks().get(i);
BlockNode block = basicBlocks.get(i);
block.setId(i);
block.setDoms(new BitSet(nBlocks));
block.getDoms().set(0, nBlocks);
......@@ -224,7 +224,7 @@ public class BlockMakerVisitor extends AbstractVisitor {
boolean changed;
do {
changed = false;
for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode block : basicBlocks) {
if (block == entryBlock)
continue;
......@@ -245,35 +245,36 @@ public class BlockMakerVisitor extends AbstractVisitor {
markLoops(mth);
// clear self dominance
for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode block : basicBlocks) {
block.getDoms().clear(block.getId());
}
// calculate immediate dominators
for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode block : basicBlocks) {
if (block == entryBlock)
continue;
if (block.getPredecessors().size() == 1) {
block.setIDom(block.getPredecessors().get(0));
List<BlockNode> preds = block.getPredecessors();
if (preds.size() == 1) {
block.setIDom(preds.get(0));
} else {
BitSet bs = new BitSet(block.getDoms().length());
bs.or(block.getDoms());
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
BlockNode dom = mth.getBasicBlocks().get(i);
BlockNode dom = basicBlocks.get(i);
bs.andNot(dom.getDoms());
}
int c = bs.cardinality();
if (c == 1) {
int id = bs.nextSetBit(0);
BlockNode idom = mth.getBasicBlocks().get(id);
BlockNode idom = basicBlocks.get(id);
block.setIDom(idom);
idom.getDominatesOn().add(block);
} else {
throw new JadxRuntimeException("Can't find immediate dominator for block " + block
+ " in " + bs + " prec:" + block.getPredecessors());
+ " in " + bs + " preds:" + preds);
}
}
}
......@@ -298,8 +299,9 @@ public class BlockMakerVisitor extends AbstractVisitor {
private static void markReturnBlocks(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
if (block.getInstructions().size() == 1) {
if (block.getInstructions().get(0).getType() == InsnType.RETURN)
List<InsnNode> insns = block.getInstructions();
if (insns.size() == 1) {
if (insns.get(0).getType() == InsnType.RETURN)
block.getAttributes().add(AttributeFlag.RETURN);
}
}
......
......@@ -10,6 +10,7 @@ import jadx.dex.nodes.MethodNode;
import jadx.dex.trycatch.CatchAttr;
import jadx.dex.trycatch.ExcHandlerAttr;
import jadx.dex.trycatch.ExceptionHandler;
import jadx.dex.trycatch.TryCatchBlock;
import jadx.utils.BlockUtils;
public class BlockProcessingHelper {
......@@ -81,8 +82,12 @@ public class BlockProcessingHelper {
if (insn.getType() == InsnType.THROW) {
CatchAttr catchAttr = (CatchAttr) insn.getAttributes().get(AttributeType.CATCH_BLOCK);
if (catchAttr != null) {
handlerAttr.getTryBlock().merge(mth, catchAttr.getTryBlock());
catchAttr.getTryBlock().removeInsn(insn);
TryCatchBlock handlerBlock = handlerAttr.getTryBlock();
TryCatchBlock catchBlock = catchAttr.getTryBlock();
if (handlerBlock != catchBlock) { // TODO: why it can be?
handlerBlock.merge(mth, catchBlock);
catchBlock.removeInsn(insn);
}
}
}
}
......
......@@ -18,7 +18,7 @@ public class ClassModifier extends AbstractVisitor {
visit(inner);
}
for (Iterator<MethodNode> it = cls.getMethods().iterator(); it.hasNext();) {
for (Iterator<MethodNode> it = cls.getMethods().iterator(); it.hasNext(); ) {
MethodNode mth = it.next();
AccessInfo af = mth.getAccessFlags();
......@@ -34,17 +34,26 @@ public class ClassModifier extends AbstractVisitor {
if (af.isConstructor()
&& af.isPublic()
&& mth.getArguments(false).isEmpty()) {
List<BlockNode> bb = mth.getBasicBlocks();
if (bb.isEmpty() || (bb.size() == 1 && bb.get(0).getInstructions().isEmpty())) {
if (mth.getSuperCall() == null)
if (mth.getSuperCall() == null) {
List<BlockNode> bb = mth.getBasicBlocks();
if (bb.isEmpty() || allBlocksEmpty(bb)) {
it.remove();
}
}
}
}
return false;
}
private boolean isMethodUniq(ClassNode cls, MethodNode mth) {
private static boolean allBlocksEmpty(List<BlockNode> blocks) {
for (BlockNode block : blocks) {
if (block.getInstructions().size() != 0)
return false;
}
return true;
}
private static boolean isMethodUniq(ClassNode cls, MethodNode mth) {
MethodInfo mi = mth.getMethodInfo();
for (MethodNode otherMth : cls.getMethods()) {
MethodInfo omi = otherMth.getMethodInfo();
......
......@@ -36,24 +36,26 @@ public class CodeShrinker extends AbstractVisitor {
private static void shrink(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
InstructionRemover remover = new InstructionRemover(block.getInstructions());
for (int i = 0; i < block.getInstructions().size(); i++) {
InsnNode insn = block.getInstructions().get(i);
List<InsnNode> insnList = block.getInstructions();
InstructionRemover remover = new InstructionRemover(insnList);
for (InsnNode insn : insnList) {
// wrap instructions
if (insn.getResult() != null) {
List<InsnArg> use = insn.getResult().getTypedVar().getUseList();
if (use.size() == 1) {
RegisterArg result = insn.getResult();
if (result != null) {
List<InsnArg> useList = result.getTypedVar().getUseList();
if (useList.size() == 1) {
// variable is used only in this instruction
// TODO not correct sometimes :(
remover.add(insn);
} else if (use.size() == 2) {
InsnArg useInsnArg = use.get(1);
} else if (useList.size() == 2) {
InsnArg useInsnArg = selectOther(useList, result);
InsnNode useInsn = useInsnArg.getParentInsn();
if (useInsn == null) {
LOG.debug("parent insn null in " + useInsnArg + " from " + insn + " mth: " + mth);
} else if (useInsn != insn) {
boolean wrap = false;
if (false && insn.getResult().getTypedVar().getName() != null) {
// TODO
if (false && result.getTypedVar().getName() != null) {
// don't wrap if result variable has name from debug info
wrap = false;
} else if (BlockUtils.blockContains(block, useInsn)) {
......@@ -181,10 +183,18 @@ public class CodeShrinker extends AbstractVisitor {
list.removeAll(args);
}
i++;
if (i > 10000)
if (i > 1000)
throw new JadxRuntimeException("Can't inline arguments for: " + arg + " insn: " + assignInsn);
} while (!list.isEmpty());
return arg.wrapInstruction(assignInsn);
}
private static InsnArg selectOther(List<InsnArg> list, RegisterArg insn) {
InsnArg first = list.get(0);
if (first == insn)
return list.get(1);
else
return first;
}
}
......@@ -22,7 +22,11 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EnumVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(EnumVisitor.class);
@Override
public boolean visit(ClassNode cls) throws JadxException {
......@@ -32,7 +36,7 @@ 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();) {
for (Iterator<FieldNode> it = cls.getFields().iterator(); it.hasNext(); ) {
FieldNode f = it.next();
if (f.getAccessFlags().isEnum()) {
enumFields.add(f);
......@@ -45,7 +49,7 @@ public class EnumVisitor extends AbstractVisitor {
MethodNode staticMethod = null;
// remove synthetic methods
for (Iterator<MethodNode> it = cls.getMethods().iterator(); it.hasNext();) {
for (Iterator<MethodNode> it = cls.getMethods().iterator(); it.hasNext(); ) {
MethodNode mth = it.next();
MethodInfo mi = mth.getMethodInfo();
if (mi.isClassInit()) {
......@@ -60,11 +64,18 @@ public class EnumVisitor extends AbstractVisitor {
}
}
if (staticMethod == null)
throw new JadxException("Enum class init method not found");
EnumClassAttr attr = new EnumClassAttr(enumFields.size());
cls.getAttributes().add(attr);
if (staticMethod == null) {
LOG.warn("Enum class init method not found: {}", cls);
// for this broken enum puts found fields and mark as inconsistent
cls.getAttributes().add(AttributeFlag.INCONSISTENT_CODE);
for (FieldNode field : enumFields) {
attr.getFields().add(new EnumField(field.getName(), 0));
}
return false;
}
attr.setStaticMethod(staticMethod);
// move enum specific instruction from static method to separate list
......@@ -117,7 +128,8 @@ public class EnumVisitor extends AbstractVisitor {
constrArg = iArg;
} else {
constrArg = CodeShrinker.inlineArgument(staticMethod, (RegisterArg) iArg);
assert constrArg != null;
if (constrArg == null)
throw new JadxException("Can't inline constructor arg in enum: " + cls);
}
field.getArgs().add(constrArg);
}
......@@ -127,7 +139,7 @@ public class EnumVisitor extends AbstractVisitor {
for (ClassNode innerCls : cls.getInnerClasses()) {
if (innerCls.getClassInfo().equals(co.getClassType())) {
// remove constructor, because it is anonymous class
for (Iterator<?> mit = innerCls.getMethods().iterator(); mit.hasNext();) {
for (Iterator<?> mit = innerCls.getMethods().iterator(); mit.hasNext(); ) {
MethodNode innerMth = (MethodNode) mit.next();
if (innerMth.getAccessFlags().isConstructor())
mit.remove();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment