Fix small issues and improve code
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -177,11 +174,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
return fallbackMode;
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
Reference in New Issue
Block a user