From b1d5ed0066a7b8a250f806d555d4145580d77dc7 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 9 May 2020 21:10:37 +0100 Subject: [PATCH] fix: don't modify method argument types in MethodInvokeVisitor (#927 #836) --- .../java/jadx/core/dex/nodes/MethodNode.java | 2 +- .../jadx/core/dex/nodes/utils/TypeUtils.java | 3 + .../dex/visitors/MethodInvokeVisitor.java | 49 ++++++++--- .../methods/MutableMethodDetails.java | 83 +++++++++++++++++++ 4 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/methods/MutableMethodDetails.java 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 6aea15ab7..ab595be06 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 @@ -181,7 +181,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, this.retType = mthInfo.getReturnType(); this.argTypes = mthInfo.getArgumentsTypes(); } else { - this.argTypes = types; + this.argTypes = Collections.unmodifiableList(types); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java index 0bb936df0..76c83ad1d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java @@ -110,6 +110,9 @@ public class TypeUtils { @Nullable public ArgType replaceTypeVariablesUsingMap(ArgType replaceType, Map replaceMap) { + if (replaceMap.isEmpty()) { + return null; + } if (replaceType.isGenericType()) { return replaceMap.get(replaceType); } 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 d44559752..24c5eeae7 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 @@ -22,6 +22,7 @@ import jadx.core.dex.nodes.IMethodDetails; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.visitors.methods.MutableMethodDetails; import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.dex.visitors.typeinference.TypeCompare; import jadx.core.dex.visitors.typeinference.TypeCompareEnum; @@ -111,12 +112,19 @@ public class MethodInvokeVisitor extends AbstractVisitor { return; } - overloadMethods.add(mthDetails); - resolveTypeVariablesInMethodArgs(invokeInsn, mthDetails, overloadMethods); + // resolve generic type variables + Map typeVarsMapping = getTypeVarsMapping(invokeInsn); + IMethodDetails effectiveMthDetails = resolveTypeVars(mthDetails, typeVarsMapping); + List effectiveOverloadMethods = new ArrayList<>(overloadMethods.size() + 1); + for (IMethodDetails overloadMethod : overloadMethods) { + effectiveOverloadMethods.add(resolveTypeVars(overloadMethod, typeVarsMapping)); + } + effectiveOverloadMethods.add(effectiveMthDetails); + // search cast types to resolve overloading int argsOffset = invokeInsn.getFirstArgOffset(); List compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset); - List castTypes = searchCastTypes(parentMth, mthDetails, overloadMethods, compilerVarTypes); + List castTypes = searchCastTypes(parentMth, effectiveMthDetails, effectiveOverloadMethods, compilerVarTypes); applyArgsCast(invokeInsn, argsOffset, compilerVarTypes, castTypes); } @@ -147,8 +155,7 @@ public class MethodInvokeVisitor extends AbstractVisitor { return callMth.getDeclClass().getType(); } - private void resolveTypeVariablesInMethodArgs(BaseInvokeNode invokeInsn, IMethodDetails mthDetails, - List overloadedMethods) { + private Map getTypeVarsMapping(BaseInvokeNode invokeInsn) { MethodInfo callMth = invokeInsn.getCallMth(); ArgType declClsType = callMth.getDeclClass().getType(); ArgType callClsType; @@ -158,12 +165,7 @@ public class MethodInvokeVisitor extends AbstractVisitor { } else { callClsType = declClsType; } - - Map typeVarsMapping = root.getTypeUtils().getTypeVariablesMapping(callClsType); - resolveTypeVars(mthDetails, typeVarsMapping); - for (IMethodDetails m : overloadedMethods) { - resolveTypeVars(m, typeVarsMapping); - } + return root.getTypeUtils().getTypeVariablesMapping(callClsType); } private void applyArgsCast(BaseInvokeNode invokeInsn, int argsOffset, List compilerVarTypes, List castTypes) { @@ -199,9 +201,11 @@ public class MethodInvokeVisitor extends AbstractVisitor { } } - private void resolveTypeVars(IMethodDetails mthDetails, Map typeVarsMapping) { + private IMethodDetails resolveTypeVars(IMethodDetails mthDetails, Map typeVarsMapping) { List argTypes = mthDetails.getArgTypes(); int argsCount = argTypes.size(); + boolean fixed = false; + List fixedArgTypes = new ArrayList<>(argsCount); for (int argNum = 0; argNum < argsCount; argNum++) { ArgType argType = argTypes.get(argNum); if (argType == null) { @@ -213,9 +217,28 @@ public class MethodInvokeVisitor extends AbstractVisitor { // type variables erased from method info by compiler resolvedType = mthDetails.getMethodInfo().getArgumentsTypes().get(argNum); } - argTypes.set(argNum, resolvedType); + fixedArgTypes.add(resolvedType); + fixed = true; + } else { + fixedArgTypes.add(argType); } } + ArgType returnType = mthDetails.getReturnType(); + if (returnType.containsTypeVariable()) { + ArgType resolvedType = root.getTypeUtils().replaceTypeVariablesUsingMap(returnType, typeVarsMapping); + if (resolvedType == null || resolvedType.containsTypeVariable()) { + returnType = mthDetails.getMethodInfo().getReturnType(); + fixed = true; + } + } + + if (!fixed) { + return mthDetails; + } + MutableMethodDetails mutableMethodDetails = new MutableMethodDetails(mthDetails); + mutableMethodDetails.setArgTypes(fixedArgTypes); + mutableMethodDetails.setRetType(returnType); + return mutableMethodDetails; } private List searchCastTypes(MethodNode parentMth, IMethodDetails mthDetails, List overloadedMethods, diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/methods/MutableMethodDetails.java b/jadx-core/src/main/java/jadx/core/dex/visitors/methods/MutableMethodDetails.java new file mode 100644 index 000000000..9b2be9855 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/methods/MutableMethodDetails.java @@ -0,0 +1,83 @@ +package jadx.core.dex.visitors.methods; + +import java.util.Collections; +import java.util.List; + +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.nodes.GenericTypeParameter; +import jadx.core.dex.nodes.IMethodDetails; + +public class MutableMethodDetails implements IMethodDetails { + + private final MethodInfo mthInfo; + private ArgType retType; + private List argTypes; + private List typeParams; + private List throwTypes; + private boolean varArg; + + public MutableMethodDetails(IMethodDetails base) { + this.mthInfo = base.getMethodInfo(); + this.retType = base.getReturnType(); + this.argTypes = Collections.unmodifiableList(base.getArgTypes()); + this.typeParams = Collections.unmodifiableList(base.getTypeParameters()); + this.throwTypes = Collections.unmodifiableList(base.getThrows()); + this.varArg = base.isVarArg(); + } + + @Override + public MethodInfo getMethodInfo() { + return mthInfo; + } + + @Override + public ArgType getReturnType() { + return retType; + } + + @Override + public List getArgTypes() { + return argTypes; + } + + @Override + public List getTypeParameters() { + return typeParams; + } + + @Override + public List getThrows() { + return throwTypes; + } + + @Override + public boolean isVarArg() { + return varArg; + } + + public void setRetType(ArgType retType) { + this.retType = retType; + } + + public void setArgTypes(List argTypes) { + this.argTypes = argTypes; + } + + public void setTypeParams(List typeParams) { + this.typeParams = typeParams; + } + + public void setThrowTypes(List throwTypes) { + this.throwTypes = throwTypes; + } + + public void setVarArg(boolean varArg) { + this.varArg = varArg; + } + + @Override + public String toString() { + return "Mutable" + toAttrString(); + } +}