fix: resolve generic types in method arguments (#913)

This commit is contained in:
Skylot
2020-05-17 14:58:34 +01:00
parent 58722d372e
commit 09e267f8bc
5 changed files with 89 additions and 10 deletions
@@ -1,5 +1,7 @@
package jadx.core.dex.attributes.nodes;
import java.util.Arrays;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttribute;
import jadx.core.dex.instructions.args.ArgType;
@@ -28,4 +30,9 @@ public class GenericInfoAttr implements IAttribute {
public AType<GenericInfoAttr> getType() {
return AType.GENERIC_INFO;
}
@Override
public String toString() {
return "GenericInfoAttr{" + Arrays.toString(genericTypes) + ", explicit=" + explicit + '}';
}
}
@@ -274,6 +274,12 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
return argTypes;
}
public void updateArgTypes(List<ArgType> newArgTypes, String comment) {
this.addDebugComment(comment + ", original types: " + getArgTypes());
this.argTypes = Collections.unmodifiableList(newArgTypes);
initArguments(newArgTypes);
}
public boolean containsGenericArgs() {
return !Objects.equals(mthInfo.getArgumentsTypes(), getArgTypes());
}
@@ -156,16 +156,20 @@ public class MethodInvokeVisitor extends AbstractVisitor {
}
private Map<ArgType, ArgType> getTypeVarsMapping(BaseInvokeNode invokeInsn) {
MethodInfo callMth = invokeInsn.getCallMth();
ArgType declClsType = callMth.getDeclClass().getType();
ArgType callClsType;
ArgType declClsType = invokeInsn.getCallMth().getDeclClass().getType();
ArgType callClsType = getClsCallType(invokeInsn, declClsType);
return root.getTypeUtils().getTypeVariablesMapping(callClsType);
}
private ArgType getClsCallType(BaseInvokeNode invokeInsn, ArgType declClsType) {
InsnArg instanceArg = invokeInsn.getInstanceArg();
if (instanceArg != null) {
callClsType = instanceArg.getType();
} else {
callClsType = declClsType;
return instanceArg.getType();
}
return root.getTypeUtils().getTypeVariablesMapping(callClsType);
if (invokeInsn.getType() == InsnType.CONSTRUCTOR && invokeInsn.getResult() != null) {
return invokeInsn.getResult().getType();
}
return declClsType;
}
private void applyArgsCast(BaseInvokeNode invokeInsn, int argsOffset, List<ArgType> compilerVarTypes, List<ArgType> castTypes) {
@@ -213,7 +217,7 @@ public class MethodInvokeVisitor extends AbstractVisitor {
}
if (argType.containsTypeVariable()) {
ArgType resolvedType = root.getTypeUtils().replaceTypeVariablesUsingMap(argType, typeVarsMapping);
if (resolvedType == null || resolvedType.containsTypeVariable()) {
if (resolvedType == null || resolvedType.equals(argType)) {
// type variables erased from method info by compiler
resolvedType = mthDetails.getMethodInfo().getArgumentsTypes().get(argNum);
}
@@ -41,6 +41,7 @@ public class OverrideMethodVisitor extends AbstractVisitor {
if (!overrideList.isEmpty()) {
mth.addAttr(new MethodOverrideAttr(overrideList));
fixMethodReturnType(mth, overrideList, superTypes);
fixMethodArgTypes(mth, overrideList, superTypes);
}
}
return true;
@@ -105,6 +106,9 @@ public class OverrideMethodVisitor extends AbstractVisitor {
private void fixMethodReturnType(MethodNode mth, List<IMethodDetails> overrideList, List<ArgType> superTypes) {
ArgType returnType = mth.getReturnType();
if (returnType == ArgType.VOID) {
return;
}
int updateCount = 0;
for (IMethodDetails baseMth : overrideList) {
if (updateReturnType(mth, baseMth, superTypes)) {
@@ -145,4 +149,61 @@ public class OverrideMethodVisitor extends AbstractVisitor {
}
return false;
}
private void fixMethodArgTypes(MethodNode mth, List<IMethodDetails> overrideList, List<ArgType> superTypes) {
for (IMethodDetails baseMth : overrideList) {
updateArgTypes(mth, baseMth, superTypes);
}
}
private void updateArgTypes(MethodNode mth, IMethodDetails baseMth, List<ArgType> superTypes) {
List<ArgType> mthArgTypes = mth.getArgTypes();
List<ArgType> baseArgTypes = baseMth.getArgTypes();
if (mthArgTypes.equals(baseArgTypes)) {
return;
}
int argCount = mthArgTypes.size();
if (argCount != baseArgTypes.size()) {
return;
}
boolean changed = false;
List<ArgType> newArgTypes = new ArrayList<>(argCount);
for (int argNum = 0; argNum < argCount; argNum++) {
ArgType newType = updateArgType(mth, baseMth, superTypes, argNum);
if (newType != null) {
changed = true;
newArgTypes.add(newType);
} else {
newArgTypes.add(mthArgTypes.get(argNum));
}
}
if (changed) {
mth.updateArgTypes(newArgTypes, "Method arguments types fixed to match base method");
}
}
private ArgType updateArgType(MethodNode mth, IMethodDetails baseMth, List<ArgType> superTypes, int argNum) {
ArgType arg = mth.getArgTypes().get(argNum);
ArgType baseArg = baseMth.getArgTypes().get(argNum);
if (arg.equals(baseArg)) {
return null;
}
if (!baseArg.containsTypeVariable()) {
return null;
}
TypeCompare typeCompare = mth.root().getTypeUpdate().getTypeCompare();
ArgType baseCls = baseMth.getMethodInfo().getDeclClass().getType();
for (ArgType superType : superTypes) {
TypeCompareEnum compareResult = typeCompare.compareTypes(superType, baseCls);
if (compareResult == TypeCompareEnum.NARROW_BY_GENERIC) {
ArgType targetArgType = mth.root().getTypeUtils().replaceClassGenerics(superType, baseArg);
if (targetArgType != null
&& !targetArgType.containsTypeVariable()
&& !targetArgType.equals(arg)) {
return targetArgType;
}
}
}
return null;
}
}
@@ -12,9 +12,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Consts;
import jadx.core.dex.instructions.BaseInvokeNode;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.PrimitiveType;
@@ -277,11 +277,12 @@ public final class TypeUpdate {
registry.put(InsnType.NOT, this::suggestAllSameListener);
registry.put(InsnType.CHECK_CAST, this::checkCastListener);
registry.put(InsnType.INVOKE, this::invokeListener);
registry.put(InsnType.CONSTRUCTOR, this::invokeListener);
return registry;
}
private TypeUpdateResult invokeListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {
InvokeNode invoke = (InvokeNode) insn;
BaseInvokeNode invoke = (BaseInvokeNode) insn;
if (isAssign(invoke, arg)) {
// TODO: implement backward type propagation (from result to instance)
return SAME;