diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java index 5466919a9..4690c2cc3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java @@ -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 getType() { return AType.GENERIC_INFO; } + + @Override + public String toString() { + return "GenericInfoAttr{" + Arrays.toString(genericTypes) + ", explicit=" + explicit + '}'; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index edca171ec..957fa0d89 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -274,6 +274,12 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, return argTypes; } + public void updateArgTypes(List 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()); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java index 24c5eeae7..e9401eedc 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java @@ -156,16 +156,20 @@ public class MethodInvokeVisitor extends AbstractVisitor { } private Map 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 compilerVarTypes, List 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); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java index 00b6743a5..865ca37c4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java @@ -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 overrideList, List 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 overrideList, List superTypes) { + for (IMethodDetails baseMth : overrideList) { + updateArgTypes(mth, baseMth, superTypes); + } + } + + private void updateArgTypes(MethodNode mth, IMethodDetails baseMth, List superTypes) { + List mthArgTypes = mth.getArgTypes(); + List baseArgTypes = baseMth.getArgTypes(); + if (mthArgTypes.equals(baseArgTypes)) { + return; + } + int argCount = mthArgTypes.size(); + if (argCount != baseArgTypes.size()) { + return; + } + boolean changed = false; + List 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 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; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java index 47bf57141..fa18c5a3e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java @@ -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;