fix: support full class name in inner generic types (#1340)
This commit is contained in:
@@ -526,12 +526,38 @@ public class ClassGen {
|
||||
if (outerType != null) {
|
||||
useClass(code, outerType);
|
||||
code.add('.');
|
||||
// import not needed, force use short name
|
||||
useClassShortName(code, type.getObject());
|
||||
addInnerType(code, type);
|
||||
return;
|
||||
}
|
||||
|
||||
useClass(code, ClassInfo.fromType(cls.root(), type));
|
||||
addGenerics(code, type);
|
||||
}
|
||||
|
||||
private void addInnerType(ICodeWriter code, ArgType baseType) {
|
||||
ArgType innerType = baseType.getInnerType();
|
||||
ArgType outerType = innerType.getOuterType();
|
||||
if (outerType != null) {
|
||||
useClass(code, outerType);
|
||||
code.add('.');
|
||||
addInnerType(code, innerType);
|
||||
return;
|
||||
}
|
||||
String fullNameObj;
|
||||
if (innerType.getObject().contains(".")) {
|
||||
fullNameObj = innerType.getObject();
|
||||
} else {
|
||||
fullNameObj = baseType.getObject();
|
||||
}
|
||||
ClassInfo classInfo = ClassInfo.fromName(cls.root(), fullNameObj);
|
||||
ClassNode classNode = cls.root().resolveClass(classInfo);
|
||||
if (classNode != null) {
|
||||
code.attachAnnotation(classNode);
|
||||
}
|
||||
code.add(classInfo.getAliasShortName());
|
||||
addGenerics(code, innerType);
|
||||
}
|
||||
|
||||
private void addGenerics(ICodeWriter code, ArgType type) {
|
||||
List<ArgType> generics = type.getGenericTypes();
|
||||
if (generics != null) {
|
||||
code.add('<');
|
||||
@@ -556,15 +582,6 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void useClassShortName(ICodeWriter code, String object) {
|
||||
ClassInfo classInfo = ClassInfo.fromName(cls.root(), object);
|
||||
ClassNode classNode = cls.root().resolveClass(classInfo);
|
||||
if (classNode != null) {
|
||||
code.attachAnnotation(classNode);
|
||||
}
|
||||
code.add(classInfo.getAliasShortName());
|
||||
}
|
||||
|
||||
public void useClass(ICodeWriter code, ClassInfo classInfo) {
|
||||
ClassNode classNode = cls.root().resolveClass(classInfo);
|
||||
if (classNode != null) {
|
||||
|
||||
@@ -246,13 +246,13 @@ public final class ClassInfo implements Comparable<ClassInfo> {
|
||||
}
|
||||
|
||||
public void notInner(RootNode root) {
|
||||
this.parentClass = null;
|
||||
splitAndApplyNames(root, type, false);
|
||||
this.parentClass = null;
|
||||
}
|
||||
|
||||
public void convertToInner(ClassNode parent) {
|
||||
this.parentClass = parent.getClassInfo();
|
||||
splitAndApplyNames(parent.root(), type, true);
|
||||
this.parentClass = parent.getClassInfo();
|
||||
}
|
||||
|
||||
public void updateNames(RootNode root) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package jadx.core.dex.nodes.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -197,6 +196,8 @@ public class SignatureParser {
|
||||
String obj = slice();
|
||||
if (!innerType) {
|
||||
obj += ';';
|
||||
} else {
|
||||
obj = obj.replace('/', '.');
|
||||
}
|
||||
List<ArgType> typeVars = consumeGenericArgs();
|
||||
consume('>');
|
||||
@@ -227,7 +228,7 @@ public class SignatureParser {
|
||||
}
|
||||
|
||||
private List<ArgType> consumeGenericArgs() {
|
||||
List<ArgType> list = new LinkedList<>();
|
||||
List<ArgType> list = new ArrayList<>();
|
||||
ArgType type;
|
||||
do {
|
||||
if (lookAhead('*')) {
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TypeUtils {
|
||||
|
||||
public ArgType expandTypeVariables(ClassNode cls, ArgType type) {
|
||||
if (type.containsTypeVariable()) {
|
||||
expandTypeVar(cls, type, cls.getGenericTypeParameters());
|
||||
expandTypeVar(cls, type, getKnownTypeVarsAtClass(cls));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@@ -115,11 +115,18 @@ public class TypeUtils {
|
||||
return varsAttr.getTypeVars();
|
||||
}
|
||||
|
||||
private static Set<ArgType> collectKnownTypeVarsAtMethod(MethodNode mth) {
|
||||
ClassNode declCls = mth.getParentClass();
|
||||
Set<ArgType> typeVars = new HashSet<>(declCls.getGenericTypeParameters());
|
||||
declCls.visitParentClasses(parent -> typeVars.addAll(parent.getGenericTypeParameters()));
|
||||
private static Collection<ArgType> getKnownTypeVarsAtClass(ClassNode cls) {
|
||||
if (cls.isInner()) {
|
||||
Set<ArgType> typeVars = new HashSet<>(cls.getGenericTypeParameters());
|
||||
cls.visitParentClasses(parent -> typeVars.addAll(parent.getGenericTypeParameters()));
|
||||
return typeVars;
|
||||
}
|
||||
return cls.getGenericTypeParameters();
|
||||
}
|
||||
|
||||
private static Set<ArgType> collectKnownTypeVarsAtMethod(MethodNode mth) {
|
||||
Set<ArgType> typeVars = new HashSet<>();
|
||||
typeVars.addAll(getKnownTypeVarsAtClass(mth.getParentClass()));
|
||||
typeVars.addAll(mth.getTypeParameters());
|
||||
return typeVars.isEmpty() ? Collections.emptySet() : typeVars;
|
||||
}
|
||||
|
||||
@@ -80,11 +80,15 @@ public class SignatureProcessor extends AbstractVisitor {
|
||||
}
|
||||
ClassNode cls = field.getParentClass();
|
||||
try {
|
||||
ArgType gType = sp.consumeType();
|
||||
if (gType == null) {
|
||||
ArgType signatureType = sp.consumeType();
|
||||
if (signatureType == null) {
|
||||
return;
|
||||
}
|
||||
ArgType type = root.getTypeUtils().expandTypeVariables(cls, gType);
|
||||
if (!validateInnerType(signatureType)) {
|
||||
field.addWarnComment("Incorrect inner types in field signature: " + sp.getSignature());
|
||||
return;
|
||||
}
|
||||
ArgType type = root.getTypeUtils().expandTypeVariables(cls, signatureType);
|
||||
if (!validateParsedType(type, field.getType())) {
|
||||
cls.addWarnComment("Incorrect field signature: " + sp.getSignature());
|
||||
return;
|
||||
@@ -105,6 +109,11 @@ public class SignatureProcessor extends AbstractVisitor {
|
||||
List<ArgType> parsedArgTypes = sp.consumeMethodArgs(mth.getMethodInfo().getArgsCount());
|
||||
ArgType parsedRetType = sp.consumeType();
|
||||
|
||||
if (!validateInnerType(parsedRetType) || !validateInnerType(parsedArgTypes)) {
|
||||
mth.addWarnComment("Incorrect inner types in method signature: " + sp.getSignature());
|
||||
return;
|
||||
}
|
||||
|
||||
mth.updateTypeParameters(typeParameters); // apply before expand args
|
||||
TypeUtils typeUtils = root.getTypeUtils();
|
||||
ArgType retType = typeUtils.expandTypeVariables(mth, parsedRetType);
|
||||
@@ -172,4 +181,54 @@ public class SignatureProcessor extends AbstractVisitor {
|
||||
TypeCompareEnum result = root.getTypeCompare().compareTypes(parsedType, currentType);
|
||||
return result != TypeCompareEnum.CONFLICT;
|
||||
}
|
||||
|
||||
private boolean validateInnerType(List<ArgType> types) {
|
||||
for (ArgType type : types) {
|
||||
if (!validateInnerType(type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateInnerType(ArgType type) {
|
||||
ArgType innerType = type.getInnerType();
|
||||
if (innerType == null) {
|
||||
return true;
|
||||
}
|
||||
// check in outer type has inner type as inner class
|
||||
ArgType outerType = type.getOuterType();
|
||||
ClassNode outerCls = root.resolveClass(outerType);
|
||||
if (outerCls == null) {
|
||||
// can't check class not found
|
||||
return true;
|
||||
}
|
||||
String innerObj;
|
||||
if (innerType.getOuterType() != null) {
|
||||
innerObj = innerType.getOuterType().getObject();
|
||||
// "next" inner type will be processed at end of method
|
||||
} else {
|
||||
innerObj = innerType.getObject();
|
||||
}
|
||||
if (!innerObj.contains(".")) {
|
||||
// short reference
|
||||
for (ClassNode innerClass : outerCls.getInnerClasses()) {
|
||||
if (innerClass.getShortName().equals(innerObj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// full name
|
||||
ClassNode innerCls = root.resolveClass(innerObj);
|
||||
if (innerCls == null) {
|
||||
return false;
|
||||
}
|
||||
if (!innerCls.getParentClass().equals(outerCls)) {
|
||||
// not inner => fixing
|
||||
outerCls.addInnerClass(innerCls);
|
||||
innerCls.getClassInfo().convertToInner(outerCls);
|
||||
}
|
||||
return validateInnerType(innerType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,19 @@ class SignatureParserTest {
|
||||
assertThat(result, equalTo(outerGeneric(outerGeneric(obj, object("I")), object("X"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedInnerGeneric2() {
|
||||
// full name in inner class
|
||||
String signature = "Lsome/long/pkg/ba<Lsome/pkg/s;>.some/long/pkg/bb<Lsome/pkg/p;Lsome/pkg/n;>;";
|
||||
ArgType result = new SignatureParser(signature).consumeType();
|
||||
System.out.println(result);
|
||||
assertThat(result.getObject(), is("some.long.pkg.ba$some.long.pkg.bb"));
|
||||
ArgType baseObj = generic("Lsome/long/pkg/ba;", object("Lsome/pkg/s;"));
|
||||
ArgType innerObj = generic("Lsome/long/pkg/bb;", object("Lsome/pkg/p;"), object("Lsome/pkg/n;"));
|
||||
ArgType obj = outerGeneric(baseObj, innerObj);
|
||||
assertThat(result, equalTo(obj));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcards() {
|
||||
checkWildcards("*", wildcard());
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package jadx.tests.integration.types;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.CommentsLevel;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestGenericsInFullInnerCls extends SmaliTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
getArgs().setCommentsLevel(CommentsLevel.WARN);
|
||||
List<ClassNode> classNodes = loadFromSmaliFiles();
|
||||
|
||||
assertThat(searchCls(classNodes, "types.FieldCls"))
|
||||
.code()
|
||||
.containsOne("private ba<n>.bb<n, n> a;");
|
||||
|
||||
assertThat(searchCls(classNodes, "types.test.ba"))
|
||||
.code()
|
||||
.containsOne("public final class ba<S> {")
|
||||
.containsOne("public final class bb<T, V extends n> {")
|
||||
.containsOne("private ba<S> b;")
|
||||
.containsOne("private ba<S>.bb<T, V>.bc<T, V> c;")
|
||||
.containsOne("public final class bc<T, V extends n> {")
|
||||
.containsOne("private ba<S> a;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithDeobf() {
|
||||
enableDeobfuscation();
|
||||
args.setDeobfuscationMinLength(100); // rename everything
|
||||
|
||||
getArgs().setCommentsLevel(CommentsLevel.WARN);
|
||||
loadFromSmaliFiles();
|
||||
// compilation should pass
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.class public Ltypes/FieldCls;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field private a:Landroidx/compose/animation/core/bb;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Ltypes/test/ba<",
|
||||
"Ltypes/n;",
|
||||
">.types/test/bb<",
|
||||
"Ltypes/n;",
|
||||
"Ltypes/n;",
|
||||
">;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
@@ -0,0 +1,11 @@
|
||||
.class public final Ltypes/test/ba;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"<S:",
|
||||
"Ljava/lang/Object;",
|
||||
">",
|
||||
"Ljava/lang/Object;"
|
||||
}
|
||||
.end annotation
|
||||
@@ -0,0 +1,33 @@
|
||||
.class public final Ltypes/test/bb;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"<T:",
|
||||
"Ljava/lang/Object;",
|
||||
"V:",
|
||||
"Ltypes/n;",
|
||||
">",
|
||||
"Ljava/lang/Object;"
|
||||
}
|
||||
.end annotation
|
||||
|
||||
.field private b:Ltypes/test/ba;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Ltypes/test/ba<",
|
||||
"TS;>;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
|
||||
.field private c:Landroidx/compose/animation/core/bc;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Ltypes/test/ba<",
|
||||
"TS;>.types/test/bb<TT;TV;>.types/test/bc<TT;TV;>;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
@@ -0,0 +1,27 @@
|
||||
.class public final Ltypes/test/bc;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"<T:",
|
||||
"Ljava/lang/Object;",
|
||||
"V:",
|
||||
"Ltypes/n;",
|
||||
">",
|
||||
"Ljava/lang/Object;",
|
||||
"Ltypes/test/ca<",
|
||||
"TT;>;"
|
||||
}
|
||||
.end annotation
|
||||
|
||||
|
||||
.field private a:Ltypes/test/ba;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Ltypes/test/ba<",
|
||||
"TS;>;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
@@ -0,0 +1,2 @@
|
||||
.class public Ltypes/n;
|
||||
.super Ljava/lang/Object;
|
||||
Reference in New Issue
Block a user