From 476b2c3735d5f0f11d816595b4ab2f75e110722c Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 4 Mar 2014 23:34:38 +0400 Subject: [PATCH] core: fix inner class handling in classpath and signature parser --- .../main/java/jadx/core/clsp/ClspGraph.java | 59 ++++++++++++------- .../java/jadx/core/dex/info/ClassInfo.java | 2 +- .../core/dex/instructions/args/ArgType.java | 16 ++++- .../core/dex/instructions/args/InsnArg.java | 10 +++- .../jadx/core/dex/nodes/parser/LocalVar.java | 26 +++++++- .../dex/visitors/regions/CheckRegions.java | 5 +- .../src/main/java/jadx/core/utils/Utils.java | 2 +- .../dex/nodes/parser/TestSignatureParser.java | 2 +- 8 files changed, 94 insertions(+), 28 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java index 609d01acc..3f6337b47 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java @@ -5,6 +5,7 @@ import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxRuntimeException; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -44,35 +45,47 @@ public class ClspGraph { throw new JadxRuntimeException("Classpath must be loaded first"); } int size = classes.size(); + for (ClassNode cls : classes) { + size += cls.getInnerClasses().size(); + } NClass[] nClasses = new NClass[size]; - for (int i = 0; i < size; i++) { - ClassNode cls = classes.get(i); - NClass nClass = new NClass(cls.getRawName(), -1); - nClasses[i] = nClass; - nameMap.put(cls.getRawName(), nClass); + int k = 0; + for (ClassNode cls : classes) { + nClasses[k++] = addClass(cls); + for (ClassNode inner : cls.getInnerClasses()) { + nClasses[k++] = addClass(inner); + } } for (int i = 0; i < size; i++) { nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap)); } } + private NClass addClass(ClassNode cls) { + NClass nClass = new NClass(cls.getRawName(), -1); + nameMap.put(cls.getRawName(), nClass); + return nClass; + } + public boolean isImplements(String clsName, String implClsName) { Set anc = getAncestors(clsName); return anc.contains(implClsName); } public String getCommonAncestor(String clsName, String implClsName) { - if (isImplements(clsName, implClsName)) { - return implClsName; + if (clsName.equals(implClsName)) { + return clsName; } - Set anc = getAncestors(clsName); NClass cls = nameMap.get(implClsName); if (cls != null) { + if (isImplements(clsName, implClsName)) { + return implClsName; + } + Set anc = getAncestors(clsName); return searchCommonParent(anc, cls); - } else { - LOG.debug("Missing class: {}", implClsName); - return null; } + LOG.debug("Missing class: {}", implClsName); + return null; } private String searchCommonParent(Set anc, NClass cls) { @@ -92,17 +105,21 @@ public class ClspGraph { private Set getAncestors(String clsName) { Set result = ancestorCache.get(clsName); - if (result == null) { - result = new HashSet(); - ancestorCache.put(clsName, result); - NClass cls = nameMap.get(clsName); - if (cls != null) { - addAncestorsNames(cls, result); - } else { - LOG.debug("Missing class: {}", clsName); - } + if (result != null) { + return result; } - return result; + NClass cls = nameMap.get(clsName); + if (cls != null) { + result = new HashSet(); + addAncestorsNames(cls, result); + if (result.isEmpty()) { + result = Collections.emptySet(); + } + ancestorCache.put(clsName, result); + return result; + } + LOG.debug("Missing class: {}", clsName); + return Collections.emptySet(); } private void addAncestorsNames(NClass cls, Set result) { diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 0ea8322e6..65417f618 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -72,7 +72,7 @@ public final class ClassInfo { int sep = clsName.lastIndexOf('$'); if (canBeInner && sep > 0 && sep != clsName.length() - 1) { - String parClsName = pkg + '.' + clsName.substring(0, sep); + String parClsName = pkg + "." + clsName.substring(0, sep); parentClass = fromName(parClsName); clsName = clsName.substring(sep + 1); } else { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index c0dbb19dc..4843d7b83 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -179,6 +179,11 @@ public abstract class ArgType { this.bounds = bound; } + @Override + public boolean isGeneric() { + return true; + } + @Override public ArgType getWildcardType() { return type; @@ -224,13 +229,18 @@ public abstract class ArgType { } public GenericObject(GenericObject outerType, String innerName, ArgType[] generics) { - super(outerType.getObject() + "." + innerName); + super(outerType.getObject() + "$" + innerName); this.outerType = outerType; this.generics = generics; this.hash = outerType.hashCode() + 31 * innerName.hashCode() + 31 * 31 * Arrays.hashCode(generics); } + @Override + public boolean isGeneric() { + return true; + } + @Override public ArgType[] getGenericTypes() { return generics; @@ -370,6 +380,10 @@ public abstract class ArgType { return false; } + public boolean isGeneric() { + return false; + } + public boolean isGenericType() { return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java index de43d27f7..a7b3c9bc3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java @@ -3,6 +3,9 @@ package jadx.core.dex.instructions.args; import jadx.core.dex.nodes.InsnNode; import jadx.core.utils.InsnUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.android.dx.io.instructions.DecodedInstruction; /** @@ -11,6 +14,8 @@ import com.android.dx.io.instructions.DecodedInstruction; */ public abstract class InsnArg extends Typed { + private static final Logger LOG = LoggerFactory.getLogger(InsnArg.class); + protected InsnNode parentInsn; public static RegisterArg reg(int regNum, ArgType type) { @@ -72,7 +77,10 @@ public abstract class InsnArg extends Typed { if (parent == null) { return null; } - assert parent != insn : "Can't wrap instruction info itself"; + if (parent == insn) { + LOG.debug("Can't wrap instruction info itself: " + insn); + return null; + } int count = parent.getArgsCount(); for (int i = 0; i < count; i++) { if (parent.getArg(i) == this) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java index 62e65c189..17db363a3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java @@ -6,8 +6,13 @@ import jadx.core.dex.instructions.args.TypedVar; import jadx.core.dex.nodes.DexNode; import jadx.core.utils.InsnUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + final class LocalVar extends RegisterArg { + private static final Logger LOG = LoggerFactory.getLogger(LocalVar.class); + private boolean isEnd; private int startAddr; @@ -29,13 +34,32 @@ final class LocalVar extends RegisterArg { private void init(String name, ArgType type, String sign) { if (sign != null) { - type = ArgType.generic(sign); + ArgType gType = ArgType.generic(sign); + if (checkSignature(type, sign, gType)) { + type = gType; + } } TypedVar tv = new TypedVar(type); tv.setName(name); forceSetTypedVar(tv); } + private boolean checkSignature(ArgType type, String sign, ArgType gType) { + boolean apply = false; + ArgType el = gType.getArrayRootElement(); + if (el.isGeneric()) { + if (!type.getObject().equals(el.getObject())) { + LOG.warn("Generic type in debug info not equals: {} != {}", type, gType); + } + apply = true; + } else if (el.isGenericType()) { + apply = true; + } else { + LOG.debug("Local var signature from debug info not generic: {}, parsed: {}", sign, gType); + } + return apply; + } + public void start(int addr, int line) { this.isEnd = false; this.startAddr = addr; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java index d63c462c9..d3f48f711 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java @@ -1,6 +1,7 @@ package jadx.core.dex.visitors.regions; import jadx.core.dex.attributes.AttributeFlag; +import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IRegion; @@ -21,7 +22,9 @@ public class CheckRegions extends AbstractVisitor { @Override public void visit(MethodNode mth) throws JadxException { - if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) { + if (mth.isNoCode() + || mth.getBasicBlocks().isEmpty() + || mth.getAttributes().contains(AttributeType.JADX_ERROR)) { return; } diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 7b99e0413..4ab8e53cd 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -8,7 +8,7 @@ import java.io.StringWriter; import java.util.Iterator; public class Utils { - + private Utils() { } diff --git a/jadx-core/src/test/java/jadx/core/dex/nodes/parser/TestSignatureParser.java b/jadx-core/src/test/java/jadx/core/dex/nodes/parser/TestSignatureParser.java index 9e8a4bb2f..9834ebbb9 100644 --- a/jadx-core/src/test/java/jadx/core/dex/nodes/parser/TestSignatureParser.java +++ b/jadx-core/src/test/java/jadx/core/dex/nodes/parser/TestSignatureParser.java @@ -50,7 +50,7 @@ public class TestSignatureParser { "c", new ArgType[]{ArgType.genericType("V")})); assertEquals(p("La.LinkedHashIterator;>;").consumeType().getObject(), - "a.LinkedHashIterator"); + "a$LinkedHashIterator"); }