提交 fef3e21c 编写于 作者: S Skylot

fix: resolve type vars from outer class (#1192)

上级 f3d76c43
......@@ -31,8 +31,12 @@ public class ClassTypeVarsAttr implements IAttribute {
return typeVars;
}
public Map<String, Map<ArgType, ArgType>> getSuperTypeMaps() {
return superTypeMaps;
public Map<ArgType, ArgType> getTypeVarsMapFor(ArgType type) {
Map<ArgType, ArgType> typeMap = superTypeMaps.get(type.getObject());
if (typeMap == null) {
return Collections.emptyMap();
}
return typeMap;
}
@Override
......
......@@ -805,6 +805,10 @@ public abstract class ArgType {
}
}
}
ArgType outerType = getOuterType();
if (outerType != null) {
return outerType.containsTypeVariable();
}
return false;
}
if (isArray()) {
......
......@@ -164,19 +164,41 @@ public class TypeUtils {
if (typeWithGeneric == null || genericSourceType == null) {
return null;
}
Map<ArgType, ArgType> typeVarsMap;
Map<ArgType, ArgType> typeVarsMap = Collections.emptyMap();
ClassTypeVarsAttr typeVars = getClassTypeVars(instanceType);
if (typeVars != null) {
typeVarsMap = typeVars.getSuperTypeMaps().get(genericSourceType.getObject());
} else {
typeVarsMap = getTypeVariablesMapping(instanceType);
typeVarsMap = mergeTypeMaps(typeVarsMap, typeVars.getTypeVarsMapFor(genericSourceType));
}
if (typeVarsMap == null) {
return null;
typeVarsMap = mergeTypeMaps(typeVarsMap, getTypeVariablesMapping(instanceType));
ArgType outerType = instanceType.getOuterType();
while (outerType != null) {
typeVarsMap = mergeTypeMaps(typeVarsMap, getTypeVariablesMapping(outerType));
outerType = outerType.getOuterType();
}
return replaceTypeVariablesUsingMap(typeWithGeneric, typeVarsMap);
}
private static Map<ArgType, ArgType> mergeTypeMaps(Map<ArgType, ArgType> base, Map<ArgType, ArgType> addition) {
if (base.isEmpty()) {
return addition;
}
if (addition.isEmpty()) {
return base;
}
Map<ArgType, ArgType> map = new HashMap<>(base.size() + addition.size());
for (Map.Entry<ArgType, ArgType> entry : base.entrySet()) {
ArgType value = entry.getValue();
ArgType type = addition.remove(value);
if (type != null) {
map.put(entry.getKey(), type);
} else {
map.put(entry.getKey(), entry.getValue());
}
}
map.putAll(addition);
return map;
}
public Map<ArgType, ArgType> getTypeVariablesMapping(ArgType clsType) {
if (!clsType.isGeneric()) {
return Collections.emptyMap();
......@@ -274,13 +296,25 @@ public class TypeUtils {
return ArgType.wildcard(newWildcardType, replaceType.getWildcardBound());
}
List<ArgType> genericTypes = replaceType.getGenericTypes();
if (replaceType.isGeneric() && notEmpty(genericTypes)) {
List<ArgType> newTypes = Utils.collectionMap(genericTypes, t -> {
ArgType type = replaceTypeVariablesUsingMap(t, replaceMap);
return type == null ? t : type;
});
return ArgType.generic(replaceType, newTypes);
if (replaceType.isGeneric()) {
ArgType outerType = replaceType.getOuterType();
if (outerType != null) {
ArgType replacedOuter = replaceTypeVariablesUsingMap(outerType, replaceMap);
if (replacedOuter == null) {
return null;
}
ArgType innerType = replaceType.getInnerType();
ArgType replacedInner = replaceTypeVariablesUsingMap(innerType, replaceMap);
return ArgType.outerGeneric(replacedOuter, replacedInner == null ? innerType : replacedInner);
}
List<ArgType> genericTypes = replaceType.getGenericTypes();
if (notEmpty(genericTypes)) {
List<ArgType> newTypes = Utils.collectionMap(genericTypes, t -> {
ArgType type = replaceTypeVariablesUsingMap(t, replaceMap);
return type == null ? t : type;
});
return ArgType.generic(replaceType, newTypes);
}
}
return null;
}
......
......@@ -36,11 +36,11 @@ class ArgTypeTest {
ArgType base = ArgType.generic("java.util.Map", genericTypes);
ArgType genericInner = ArgType.outerGeneric(base, ArgType.generic("Entry", genericTypes));
LOG.debug("genericInner : {}", genericInner);
assertThat(genericInner.toString(), is("java.util.Map<K, V>$Entry<K, V>"));
assertTrue(genericInner.containsTypeVariable());
ArgType genericInner2 = ArgType.outerGeneric(base, ArgType.object("Entry"));
LOG.debug("genericInner2: {}", genericInner2);
assertThat(genericInner.toString(), is("java.util.Map<K, V>$Entry<K, V>"));
assertThat(genericInner2.toString(), is("java.util.Map<K, V>$Entry"));
assertTrue(genericInner2.containsTypeVariable());
}
}
package jadx.core.dex.nodes.utils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
......@@ -10,11 +11,13 @@ import jadx.api.JadxArgs;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.RootNode;
import static jadx.core.dex.instructions.args.ArgType.EXCEPTION;
import static jadx.core.dex.instructions.args.ArgType.STRING;
import static jadx.core.dex.instructions.args.ArgType.array;
import static jadx.core.dex.instructions.args.ArgType.generic;
import static jadx.core.dex.instructions.args.ArgType.genericType;
import static jadx.core.dex.instructions.args.ArgType.object;
import static jadx.core.dex.instructions.args.ArgType.outerGeneric;
import static org.assertj.core.api.Assertions.assertThat;
class TypeUtilsTest {
......@@ -36,6 +39,26 @@ class TypeUtilsTest {
replaceTypeVar(array(typeVar), typeMap, array(STRING));
}
@Test
void replaceTypeVariablesUsingMap2() {
ArgType kVar = genericType("K");
ArgType vVar = genericType("V");
ArgType mapCls = object("java.util.Map");
ArgType entryCls = object("Entry");
ArgType typedMap = generic(mapCls, kVar, vVar);
ArgType typedEntry = generic(entryCls, kVar, vVar);
Map<ArgType, ArgType> typeMap = new HashMap<>();
typeMap.put(kVar, STRING);
typeMap.put(vVar, EXCEPTION);
ArgType replacedMap = typeUtils.replaceTypeVariablesUsingMap(typedMap, typeMap);
ArgType replacedEntry = typeUtils.replaceTypeVariablesUsingMap(typedEntry, typeMap);
replaceTypeVar(outerGeneric(typedMap, entryCls), typeMap, outerGeneric(replacedMap, entryCls));
replaceTypeVar(outerGeneric(typedMap, typedEntry), typeMap, outerGeneric(replacedMap, replacedEntry));
}
private void replaceTypeVar(ArgType typeVar, Map<ArgType, ArgType> typeMap, ArgType expected) {
ArgType resultType = typeUtils.replaceTypeVariablesUsingMap(typeVar, typeMap);
assertThat(resultType)
......
package jadx.tests.integration.generics;
import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestTypeVarsFromOuterClass extends IntegrationTest {
public static class TestCls {
public interface I<X> {
Map.Entry<X, X> entry();
}
public static class Outer<Y> {
public class Inner implements I<Y> {
@Override
public Map.Entry<Y, Y> entry() {
return null;
}
}
public Inner getInner() {
return null;
}
}
private Outer<String> outer;
public void test() {
Outer<String>.Inner inner = this.outer.getInner();
use(inner, inner);
Map.Entry<String, String> entry = inner.entry();
use(entry.getKey(), entry.getValue());
}
public void test2() {
// force interface virtual call
I<String> base = this.outer.getInner();
use(base, base);
Map.Entry<String, String> entry = base.entry();
use(entry.getKey(), entry.getValue());
}
public void use(Object a, Object b) {
}
}
@Test
public void test() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("Outer<Y>.Inner inner")
.doesNotContain("Object entry = ")
.countString(2, "Outer<String>.Inner inner = this.outer.getInner();")
.countString(2, "Map.Entry<String, String> entry = ");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册