fix: resolve generic types in method arguments (#913)
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user