fix: handle unbound type variables in type inference (#1237)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -16,8 +17,6 @@ import jadx.core.dex.visitors.typeinference.TypeCompareEnum;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
public class SignatureProcessor extends AbstractVisitor {
|
||||
|
||||
private RootNode root;
|
||||
@@ -106,25 +105,38 @@ public class SignatureProcessor extends AbstractVisitor {
|
||||
List<ArgType> parsedArgTypes = sp.consumeMethodArgs(mth.getMethodInfo().getArgsCount());
|
||||
ArgType parsedRetType = sp.consumeType();
|
||||
|
||||
if (!validateParsedType(parsedRetType, mth.getMethodInfo().getReturnType())) {
|
||||
mth.addWarnComment("Incorrect return type in method signature: " + sp.getSignature());
|
||||
return;
|
||||
}
|
||||
List<ArgType> checkedArgTypes = checkArgTypes(mth, sp, parsedArgTypes);
|
||||
if (checkedArgTypes == null) {
|
||||
return;
|
||||
}
|
||||
mth.updateTypeParameters(typeParameters); // apply before expand args
|
||||
|
||||
TypeUtils typeUtils = root.getTypeUtils();
|
||||
ArgType retType = typeUtils.expandTypeVariables(mth, parsedRetType);
|
||||
List<ArgType> resultArgTypes = Utils.collectionMap(checkedArgTypes, t -> typeUtils.expandTypeVariables(mth, t));
|
||||
mth.updateTypes(unmodifiableList(resultArgTypes), retType);
|
||||
List<ArgType> argTypes = Utils.collectionMap(parsedArgTypes, t -> typeUtils.expandTypeVariables(mth, t));
|
||||
|
||||
if (!validateAndApplyTypes(mth, sp, retType, argTypes)) {
|
||||
// bad types -> reset typed parameters
|
||||
mth.updateTypeParameters(Collections.emptyList());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
mth.addWarnComment("Failed to parse method signature: " + sp.getSignature(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateAndApplyTypes(MethodNode mth, SignatureParser sp, ArgType retType, List<ArgType> argTypes) {
|
||||
try {
|
||||
if (!validateParsedType(retType, mth.getMethodInfo().getReturnType())) {
|
||||
mth.addWarnComment("Incorrect return type in method signature: " + sp.getSignature());
|
||||
return false;
|
||||
}
|
||||
List<ArgType> checkedArgTypes = checkArgTypes(mth, sp, argTypes);
|
||||
if (checkedArgTypes == null) {
|
||||
return false;
|
||||
}
|
||||
mth.updateTypes(Utils.lockList(checkedArgTypes), retType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
mth.addWarnComment("Type validation failed for signature: " + sp.getSignature(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private List<ArgType> checkArgTypes(MethodNode mth, SignatureParser sp, List<ArgType> parsedArgTypes) {
|
||||
MethodInfo mthInfo = mth.getMethodInfo();
|
||||
List<ArgType> mthArgTypes = mthInfo.getArgumentsTypes();
|
||||
|
||||
@@ -245,12 +245,12 @@ public class TypeCompare {
|
||||
if (objType.isGenericType()) {
|
||||
return compareTypeVariables(genericType, objType);
|
||||
}
|
||||
|
||||
boolean rootObject = objType.equals(ArgType.OBJECT);
|
||||
List<ArgType> extendTypes = genericType.getExtendTypes();
|
||||
if (extendTypes.isEmpty()) {
|
||||
return NARROW;
|
||||
return rootObject ? NARROW : CONFLICT;
|
||||
}
|
||||
if (extendTypes.contains(objType) || objType.equals(ArgType.OBJECT)) {
|
||||
if (extendTypes.contains(objType) || rootObject) {
|
||||
return NARROW;
|
||||
}
|
||||
for (ArgType extendType : extendTypes) {
|
||||
|
||||
@@ -138,7 +138,7 @@ public class TypeCompareTest {
|
||||
public void compareGenericTypes() {
|
||||
ArgType vType = genericType("V");
|
||||
check(vType, OBJECT, TypeCompareEnum.NARROW);
|
||||
check(vType, STRING, TypeCompareEnum.NARROW);
|
||||
check(vType, STRING, TypeCompareEnum.CONFLICT);
|
||||
|
||||
ArgType rType = genericType("R");
|
||||
check(vType, rType, TypeCompareEnum.CONFLICT);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package jadx.tests.integration.types;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestTypeResolver18 extends IntegrationTest {
|
||||
|
||||
public static class TestCls<T> {
|
||||
private final AtomicReference<T> reference = new AtomicReference<>();
|
||||
|
||||
public void test() {
|
||||
T t = this.reference.get();
|
||||
if (t instanceof Closeable) {
|
||||
try {
|
||||
((Closeable) t).close();
|
||||
} catch (IOException unused) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
this.reference.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("((Closeable) t).close();");
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"Lapp/ItemCurrency;",
|
||||
"Lapp/ItemCurrency;",
|
||||
"Lapp/ItemCurrency;",
|
||||
"ZTItem;)Z"
|
||||
"ZLapp/SearchListItem;)Z"
|
||||
}
|
||||
.end annotation
|
||||
|
||||
|
||||
Reference in New Issue
Block a user