package jadx.core.dex.info; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; import jadx.core.utils.exceptions.JadxRuntimeException; import java.io.File; public final class ClassInfo { private final ArgType type; private String pkg; private String name; private String fullName; // for inner class not equals null private ClassInfo parentClass; // class info after rename (deobfuscation) private ClassInfo alias; private ClassInfo(DexNode dex, ArgType type) { this(dex, type, true); } private ClassInfo(DexNode dex, ArgType type, boolean inner) { if (!type.isObject() || type.isGeneric()) { throw new JadxRuntimeException("Not class type: " + type); } this.type = type; this.alias = this; splitNames(dex, inner); } public static ClassInfo fromType(DexNode dex, ArgType type) { if (type.isArray()) { type = ArgType.OBJECT; } ClassInfo cls = dex.getInfoStorage().getCls(type); if (cls != null) { return cls; } cls = new ClassInfo(dex, type); return dex.getInfoStorage().putCls(cls); } public static ClassInfo fromDex(DexNode dex, int clsIndex) { if (clsIndex == DexNode.NO_INDEX) { return null; } return fromType(dex, dex.getType(clsIndex)); } public static ClassInfo fromName(DexNode dex, String clsName) { return fromType(dex, ArgType.object(clsName)); } public static ClassInfo extCls(DexNode dex, ArgType type) { ClassInfo classInfo = fromName(dex, type.getObject()); return classInfo.alias; } public void rename(DexNode dex, String fullName) { ClassInfo newAlias = new ClassInfo(dex, ArgType.object(fullName), isInner()); if (!alias.getFullName().equals(newAlias.getFullName())) { this.alias = newAlias; } } public boolean isRenamed() { return alias != this; } public ClassInfo getAlias() { return alias; } private void splitNames(DexNode dex, boolean canBeInner) { String fullObjectName = type.getObject(); String clsName; int dot = fullObjectName.lastIndexOf('.'); if (dot == -1) { pkg = ""; clsName = fullObjectName; } else { pkg = fullObjectName.substring(0, dot); clsName = fullObjectName.substring(dot + 1); } int sep = clsName.lastIndexOf('$'); if (canBeInner && sep > 0 && sep != clsName.length() - 1) { String parClsName = pkg + "." + clsName.substring(0, sep); parentClass = fromName(dex, parClsName); clsName = clsName.substring(sep + 1); } else { parentClass = null; } this.name = clsName; this.fullName = makeFullClsName(clsName, false); } public String makeFullClsName(String shortName, boolean raw) { if (parentClass != null) { String innerSep = raw ? "$" : "."; return parentClass.makeFullClsName(parentClass.getShortName(), raw) + innerSep + shortName; } return pkg.isEmpty() ? shortName : pkg + "." + shortName; } public String getFullPath() { ClassInfo alias = getAlias(); return alias.getPackage().replace('.', File.separatorChar) + File.separatorChar + alias.getNameWithoutPackage().replace('.', '_'); } public String getFullName() { return fullName; } public String getShortName() { return name; } public String getPackage() { return pkg; } public String getRawName() { return type.getObject(); } public String getNameWithoutPackage() { if (parentClass == null) { return name; } return parentClass.getNameWithoutPackage() + "." + name; } public ClassInfo getParentClass() { return parentClass; } public ClassInfo getTopParentClass() { if (parentClass != null) { ClassInfo topCls = parentClass.getTopParentClass(); return topCls != null ? topCls : parentClass; } return null; } public boolean isInner() { return parentClass != null; } public void notInner(DexNode dex) { splitNames(dex, false); } public ArgType getType() { return type; } @Override public String toString() { return fullName; } @Override public int hashCode() { return fullName.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof ClassInfo) { ClassInfo other = (ClassInfo) obj; return this.type.equals(other.type); } return false; } }