diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index c6c6783c8..288f81c64 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -36,6 +36,7 @@ import jadx.core.utils.CodegenUtils; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.CodegenException; +import jadx.core.utils.exceptions.JadxRuntimeException; public class ClassGen { @@ -141,7 +142,7 @@ public class ClassGen { clsCode.add("class "); } clsCode.attachDefinition(cls); - clsCode.add(cls.getShortName()); + clsCode.add(cls.getAlias().getShortName()); addGenericMap(clsCode, cls.getGenericMap(), true); clsCode.add(' '); @@ -199,9 +200,8 @@ public class ClassGen { code.add(g.getObject()); } else { useClass(code, g); - if (classDeclaration && !cls.getAlias().isInner()) { - addImport(ClassInfo.extCls(cls.root(), g)); + addImport(ClassInfo.fromType(cls.root(), g)); } } if (it.hasNext()) { @@ -462,7 +462,7 @@ public class ClassGen { } public void useClass(CodeWriter code, ArgType type) { - useClass(code, ClassInfo.extCls(cls.root(), type)); + useClass(code, ClassInfo.fromType(cls.root(), type)); ArgType[] generics = type.getGenericTypes(); if (generics != null) { code.add('<'); @@ -503,16 +503,16 @@ public class ClassGen { } private void addClsName(CodeWriter code, ClassInfo classInfo) { - String clsName = useClassInternal(cls.getAlias(), classInfo.getAlias()); + String clsName = useClassInternal(cls.getClassInfo(), classInfo); code.add(clsName); } private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) { - String fullName = extClsInfo.getFullName(); + String fullName = extClsInfo.getAlias().makeFullName(); if (fallback || !useImports) { return fullName; } - String shortName = extClsInfo.getShortName(); + String shortName = extClsInfo.getAlias().getShortName(); if (extClsInfo.getPackage().equals("java.lang") && extClsInfo.getParentClass() == null) { return shortName; } @@ -538,14 +538,14 @@ public class ClassGen { if (extClsInfo.isDefaultPackage()) { return shortName; } - if (extClsInfo.getPackage().equals(useCls.getPackage())) { - fullName = extClsInfo.getNameWithoutPackage(); + if (extClsInfo.getAlias().getPackage().equals(useCls.getAlias().getPackage())) { + fullName = extClsInfo.getAlias().getNameWithoutPackage(); } for (ClassInfo importCls : getImports()) { if (!importCls.equals(extClsInfo) - && importCls.getShortName().equals(shortName)) { + && importCls.getAlias().getShortName().equals(shortName)) { if (extClsInfo.isInner()) { - String parent = useClassInternal(useCls, extClsInfo.getParentClass().getAlias()); + String parent = useClassInternal(useCls, extClsInfo.getParentClass()); return parent + '.' + shortName; } else { return fullName; @@ -558,8 +558,11 @@ public class ClassGen { private void addImport(ClassInfo classInfo) { if (parentGen != null) { - parentGen.addImport(classInfo.getAlias()); + parentGen.addImport(classInfo); } else { + if (classInfo.isAlias()) { + throw new JadxRuntimeException("Don't add aliases class info to import list: " + classInfo); + } imports.add(classInfo); } } @@ -594,15 +597,15 @@ public class ClassGen { if (useCls == null) { return false; } - String shortName = searchCls.getShortName(); - if (useCls.getShortName().equals(shortName)) { + String shortName = searchCls.getAlias().getShortName(); + if (useCls.getAlias().getShortName().equals(shortName)) { return true; } ClassNode classNode = dex.resolveClass(useCls); if (classNode != null) { for (ClassNode inner : classNode.getInnerClasses()) { - if (inner.getShortName().equals(shortName) - && !inner.getAlias().equals(searchCls)) { + if (inner.getAlias().getShortName().equals(shortName) + && !inner.getAlias().equals(searchCls.getAlias())) { return true; } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 7e5a2887f..2625591be 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -13,7 +13,6 @@ import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.nodes.JumpInfo; import jadx.core.dex.info.AccessInfo; -import jadx.core.dex.info.ClassInfo; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; @@ -180,10 +179,8 @@ public class MethodGen { addFallbackMethodCode(code); code.startLine("*/"); - ClassInfo clsAlias = mth.getParentClass().getAlias(); - code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ") - .add(clsAlias.makeFullClsName(clsAlias.getShortName(), true)) + .add(mth.getParentClass().getAlias().makeFullName()) .add('.') .add(mth.getAlias()) .add('(') diff --git a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java index b1588a935..80574edce 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java @@ -177,12 +177,15 @@ public class NameGen { } private String makeNameForObject(ArgType type) { + if (type.isGenericType()) { + return StringUtils.escape(type.getObject().toLowerCase()); + } if (type.isObject()) { String alias = getAliasForObject(type.getObject()); if (alias != null) { return alias; } - ClassInfo extClsInfo = ClassInfo.extCls(mth.root(), type); + ClassInfo extClsInfo = ClassInfo.fromType(mth.root(), type); String shortName = extClsInfo.getShortName(); String vName = fromName(shortName); if (vName != null) { diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 4a85a69d7..b01bae568 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -232,7 +232,7 @@ public class Deobfuscator { ClassInfo clsInfo = cls.getClassInfo(); String fullName = getClassFullName(clsInfo); if (!fullName.equals(clsInfo.getFullName())) { - clsInfo.rename(cls.dex().root(), fullName); + clsInfo.rename(cls.root(), fullName); } for (FieldNode field : cls.getFields()) { if (field.contains(AFlag.DONT_RENAME)) { 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 7992e9a32..9d1b8cffa 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 @@ -1,8 +1,10 @@ package jadx.core.dex.info; import java.io.File; +import java.util.Objects; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; @@ -20,30 +22,53 @@ public final class ClassInfo implements Comparable { // class info after rename (deobfuscation) private ClassInfo alias; + private ClassInfo(ArgType type) { + this.type = checkClassType(type); + this.alias = this; + } + private ClassInfo(RootNode root, ArgType type) { this(root, type, true); } private ClassInfo(RootNode root, ArgType type, boolean inner) { - if (!type.isObject() || type.isGeneric()) { - throw new JadxRuntimeException("Not class type: " + type); - } - this.type = type; - this.alias = this; + this(type); + splitAndApplyNames(root, type, inner); + } - splitNames(root, inner); + private ClassInfo(ArgType type, String pkg, String name, @Nullable ClassInfo parentClass) { + this(type); + this.pkg = pkg; + this.name = name; + this.parentClass = parentClass; + this.fullName = makeFullClsName(name, false); } public static ClassInfo fromType(RootNode root, ArgType type) { - if (type.isArray()) { - type = ArgType.OBJECT; - } - ClassInfo cls = root.getInfoStorage().getCls(type); + ArgType clsType = checkClassType(type); + ClassInfo cls = root.getInfoStorage().getCls(clsType); if (cls != null) { return cls; } - cls = new ClassInfo(root, type); - return root.getInfoStorage().putCls(cls); + ClassInfo newClsInfo = new ClassInfo(root, clsType); + return root.getInfoStorage().putCls(newClsInfo); + } + + private static ArgType checkClassType(ArgType type) { + if (type == null) { + throw new JadxRuntimeException("Null class type"); + } + if (type.isArray()) { + // TODO: check case with method declared in array class like ( clone in int[]) + return ArgType.OBJECT; + } + if (!type.isObject() || type.isGenericType()) { + throw new JadxRuntimeException("Not class type: " + type); + } + if (type.isGeneric()) { + return ArgType.object(type.getObject()); + } + return type; } public static ClassInfo fromDex(DexNode dex, int clsIndex) { @@ -57,19 +82,27 @@ public final class ClassInfo implements Comparable { return fromType(root, ArgType.object(clsName)); } - public static ClassInfo extCls(RootNode root, ArgType type) { - ClassInfo classInfo = fromName(root, type.getObject()); - return classInfo.alias; + public void rename(RootNode root, String fullName) { + if (!alias.makeFullName().equals(fullName)) { + ClassInfo newAlias = new ClassInfo(type); + newAlias.splitAndApplyNames(root, fullName, isInner()); + newAlias.alias = null; + this.alias = newAlias; + } } - public void rename(RootNode root, String fullName) { - ArgType clsType = ArgType.object(fullName); - ClassInfo newAlias = root.getInfoStorage().getCls(clsType); - if (newAlias == null) { - newAlias = new ClassInfo(root, clsType, isInner()); - root.getInfoStorage().putCls(newAlias); + public void renameShortName(String aliasName) { + if (!Objects.equals(name, aliasName)) { + ClassInfo newAlias = new ClassInfo(type, alias.pkg, aliasName, parentClass); + newAlias.alias = null; + this.alias = newAlias; } - if (!alias.getFullName().equals(newAlias.getFullName())) { + } + + public void renamePkg(String aliasPkg) { + if (!Objects.equals(pkg, aliasPkg)) { + ClassInfo newAlias = new ClassInfo(type, aliasPkg, alias.name, parentClass); + newAlias.alias = null; this.alias = newAlias; } } @@ -78,11 +111,18 @@ public final class ClassInfo implements Comparable { } public ClassInfo getAlias() { - return alias; + return alias == null ? this : alias; } - private void splitNames(RootNode root, boolean canBeInner) { - String fullObjectName = type.getObject(); + public boolean isAlias() { + return alias == null; + } + + private void splitAndApplyNames(RootNode root, ArgType type, boolean canBeInner) { + splitAndApplyNames(root, type.getObject(), canBeInner); + } + + private void splitAndApplyNames(RootNode root, String fullObjectName, boolean canBeInner) { String clsName; int dot = fullObjectName.lastIndexOf('.'); if (dot == -1) { @@ -109,7 +149,7 @@ public final class ClassInfo implements Comparable { this.fullName = makeFullClsName(clsName, false); } - public String makeFullClsName(String shortName, boolean raw) { + private String makeFullClsName(String shortName, boolean raw) { if (parentClass != null) { String innerSep = raw ? "$" : "."; return parentClass.makeFullClsName(parentClass.getShortName(), raw) + innerSep + shortName; @@ -117,6 +157,10 @@ public final class ClassInfo implements Comparable { return pkg.isEmpty() ? shortName : pkg + '.' + shortName; } + public String makeFullName() { + return makeFullClsName(this.name, false); + } + public String makeRawFullName() { return makeFullClsName(this.name, true); } @@ -129,7 +173,7 @@ public final class ClassInfo implements Comparable { } public String getFullName() { - return fullName; + return makeFullName(); } public String getShortName() { @@ -172,11 +216,12 @@ public final class ClassInfo implements Comparable { } public void notInner(RootNode root) { - splitNames(root, false); + this.parentClass = null; + splitAndApplyNames(root, type, false); } public void updateNames(RootNode root) { - splitNames(root, isInner()); + splitAndApplyNames(root, type, isInner()); } public ArgType getType() { @@ -185,7 +230,7 @@ public final class ClassInfo implements Comparable { @Override public String toString() { - return fullName; + return makeFullName(); } @Override diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java index c5880b017..cb64ad581 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java @@ -24,6 +24,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; +import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.DexFile; public class DexNode implements IDexNode { @@ -93,6 +94,9 @@ public class DexNode implements IDexNode { @Nullable ClassNode resolveClassLocal(ClassInfo clsInfo) { + if (clsInfo.isAlias()) { + throw new JadxRuntimeException("Don't resolve class by alias: " + clsInfo); + } return clsMap.get(clsInfo); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java index b1fce3f71..5f59449ab 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java @@ -114,8 +114,8 @@ public class DependencyCollector extends AbstractVisitor { private static void addDep(DexNode dex, Set depList, ArgType type) { if (type != null) { - if (type.isObject()) { - addDep(dex, depList, ClassInfo.fromName(dex.root(), type.getObject())); + if (type.isObject() && !type.isGenericType()) { + addDep(dex, depList, ClassInfo.fromType(dex.root(), type)); ArgType[] genericTypes = type.getGenericTypes(); if (type.isGeneric() && genericTypes != null) { for (ArgType argType : genericTypes) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index ae2f1b5a1..8df1daea6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -60,9 +60,7 @@ public class RenameVisitor extends AbstractVisitor { ClassInfo aliasClsInfo = clsInfo.getAlias(); if (!clsFullPaths.add(aliasClsInfo.getFullPath().toLowerCase())) { String newShortName = deobfuscator.getClsAlias(cls); - String newFullName = aliasClsInfo.makeFullClsName(newShortName, true); - - clsInfo.rename(root, newFullName); + clsInfo.renameShortName(newShortName); clsFullPaths.add(clsInfo.getAlias().getFullPath().toLowerCase()); } } @@ -71,18 +69,14 @@ public class RenameVisitor extends AbstractVisitor { private void checkClassName(ClassNode cls, JadxArgs args) { ClassInfo classInfo = cls.getClassInfo(); - ClassInfo alias = classInfo.getAlias(); - String clsName = alias.getShortName(); + String clsName = classInfo.getAlias().getShortName(); String newShortName = fixClsShortName(args, clsName); if (!newShortName.equals(clsName)) { - classInfo.rename(cls.root(), alias.makeFullClsName(newShortName, true)); - alias = classInfo.getAlias(); + classInfo.renameShortName(newShortName); } - if (alias.getPackage().isEmpty()) { - String fullName = alias.makeFullClsName(alias.getShortName(), true); - String newFullName = Consts.DEFAULT_PACKAGE_NAME + '.' + fullName; - classInfo.rename(cls.root(), newFullName); + if (classInfo.getAlias().getPackage().isEmpty()) { + classInfo.renamePkg(Consts.DEFAULT_PACKAGE_NAME); } }