fix: don't modify method argument types in MethodInvokeVisitor (#927 #836)

This commit is contained in:
Skylot
2020-05-09 21:10:37 +01:00
parent e22474e0a7
commit b1d5ed0066
4 changed files with 123 additions and 14 deletions
@@ -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);
}
}
@@ -110,6 +110,9 @@ public class TypeUtils {
@Nullable
public ArgType replaceTypeVariablesUsingMap(ArgType replaceType, Map<ArgType, ArgType> replaceMap) {
if (replaceMap.isEmpty()) {
return null;
}
if (replaceType.isGenericType()) {
return replaceMap.get(replaceType);
}
@@ -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<ArgType, ArgType> typeVarsMapping = getTypeVarsMapping(invokeInsn);
IMethodDetails effectiveMthDetails = resolveTypeVars(mthDetails, typeVarsMapping);
List<IMethodDetails> 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<ArgType> compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset);
List<ArgType> castTypes = searchCastTypes(parentMth, mthDetails, overloadMethods, compilerVarTypes);
List<ArgType> 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<IMethodDetails> overloadedMethods) {
private Map<ArgType, ArgType> 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<ArgType, ArgType> 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<ArgType> compilerVarTypes, List<ArgType> castTypes) {
@@ -199,9 +201,11 @@ public class MethodInvokeVisitor extends AbstractVisitor {
}
}
private void resolveTypeVars(IMethodDetails mthDetails, Map<ArgType, ArgType> typeVarsMapping) {
private IMethodDetails resolveTypeVars(IMethodDetails mthDetails, Map<ArgType, ArgType> typeVarsMapping) {
List<ArgType> argTypes = mthDetails.getArgTypes();
int argsCount = argTypes.size();
boolean fixed = false;
List<ArgType> 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<ArgType> searchCastTypes(MethodNode parentMth, IMethodDetails mthDetails, List<IMethodDetails> overloadedMethods,
@@ -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<ArgType> argTypes;
private List<GenericTypeParameter> typeParams;
private List<ArgType> 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<ArgType> getArgTypes() {
return argTypes;
}
@Override
public List<GenericTypeParameter> getTypeParameters() {
return typeParams;
}
@Override
public List<ArgType> getThrows() {
return throwTypes;
}
@Override
public boolean isVarArg() {
return varArg;
}
public void setRetType(ArgType retType) {
this.retType = retType;
}
public void setArgTypes(List<ArgType> argTypes) {
this.argTypes = argTypes;
}
public void setTypeParams(List<GenericTypeParameter> typeParams) {
this.typeParams = typeParams;
}
public void setThrowTypes(List<ArgType> throwTypes) {
this.throwTypes = throwTypes;
}
public void setVarArg(boolean varArg) {
this.varArg = varArg;
}
@Override
public String toString() {
return "Mutable" + toAttrString();
}
}