Skip to content

Commit

Permalink
fix: try to resolve generic type variables (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Aug 15, 2019
1 parent 06f26ef commit db2b537
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 94 deletions.
69 changes: 39 additions & 30 deletions jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.ArithNode;
import jadx.core.dex.instructions.ArithOp;
import jadx.core.dex.instructions.CallMthInterface;
import jadx.core.dex.instructions.ConstClassNode;
import jadx.core.dex.instructions.ConstStringNode;
import jadx.core.dex.instructions.FillArrayNode;
Expand Down Expand Up @@ -52,6 +53,7 @@
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.TypeUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;

Expand Down Expand Up @@ -589,6 +591,9 @@ private void filledNewArray(FilledNewArrayNode insn, CodeWriter code) throws Cod
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
throws CodegenException {
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
if (cls != null) {
cls.loadAndProcess();
}
if (cls != null && cls.isAnonymous() && !fallback) {
inlineAnonymousConstructor(code, cls, insn);
return;
Expand All @@ -604,7 +609,10 @@ private void makeConstructor(ConstructorInsn insn, CodeWriter code)
code.add("new ");
useClass(code, insn.getClassType());
ArgType argType = insn.getResult().getSVar().getCodeVar().getType();
if (argType != null && argType.isGeneric()) {
boolean genericCls = cls == null || !cls.getGenerics().isEmpty();
if (argType != null
&& argType.getGenericTypes() != null
&& genericCls) {
code.add('<');
if (insn.contains(AFlag.EXPLICIT_GENERICS)) {
boolean first = true;
Expand Down Expand Up @@ -763,7 +771,7 @@ void generateMethodArguments(CodeWriter code, InsnNode insn, int startArgNum,
if (!firstArg) {
code.add(", ");
}
boolean cast = overloaded && processOverloadedArg(code, callMth, arg, i - startArgNum);
boolean cast = overloaded && processOverloadedArg(code, insn, callMth, arg, i - startArgNum);
if (!cast && i == argsCount - 1 && processVarArg(code, callMth, arg)) {
continue;
}
Expand All @@ -774,21 +782,10 @@ void generateMethodArguments(CodeWriter code, InsnNode insn, int startArgNum,
code.add(')');
}

private static RegisterArg getCallMthArg(@Nullable MethodNode callMth, int num) {
if (callMth == null) {
return null;
}
List<RegisterArg> args = callMth.getArgRegs();
if (args != null && num < args.size()) {
return args.get(num);
}
return null;
}

/**
* Add additional cast for overloaded method argument.
*/
private boolean processOverloadedArg(CodeWriter code, MethodNode callMth, InsnArg arg, int origPos) {
private boolean processOverloadedArg(CodeWriter code, InsnNode insn, MethodNode callMth, InsnArg arg, int origPos) {
List<ArgType> argTypes = callMth.getArgTypes();
if (argTypes == null) {
// try to load class
Expand All @@ -806,9 +803,35 @@ private boolean processOverloadedArg(CodeWriter code, MethodNode callMth, InsnAr
return false;
}
}
if (isCastNeeded(arg, origType)) {
ArgType castType = null;
if (insn instanceof CallMthInterface && origType.containsGenericType()) {
ArgType clsType;
CallMthInterface mthCall = (CallMthInterface) insn;
RegisterArg instanceArg = mthCall.getInstanceArg();
if (instanceArg != null) {
clsType = instanceArg.getType();
} else {
clsType = mthCall.getCallMth().getDeclClass().getType();
}
ArgType replacedType = TypeUtils.replaceClassGenerics(root, clsType, origType);
if (replacedType != null) {
castType = replacedType;
}
if (castType == null) {
ArgType invReplType = TypeUtils.replaceMethodGenerics(root, insn, origType);
if (invReplType != null) {
castType = invReplType;
}
}
}
if (castType == null) {
castType = origType;
}
// TODO: check castType for left type variables

if (isCastNeeded(arg, castType)) {
code.add('(');
useType(code, origType);
useType(code, castType);
code.add(") ");
return true;
}
Expand All @@ -824,20 +847,6 @@ private boolean isCastNeeded(InsnArg arg, ArgType origType) {
if (argType.equals(origType)) {
return false;
}
if (origType.isGeneric()) {
if (argType.isObject()) {
if (!argType.isGeneric() && arg.isInsnWrap()) {
((InsnWrapArg) arg).getWrapInsn().getResult().setType(
ArgType.generic(argType.getObject(), origType.getGenericTypes()));
}
if (origType.getObject().equals(argType.getObject())) {
return false;
}
}
if (arg.isInsnWrap()) {
((InsnWrapArg) arg).getWrapInsn().add(AFlag.EXPLICIT_GENERICS);
}
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package jadx.core.dex.instructions;

import org.jetbrains.annotations.Nullable;

import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.RegisterArg;

public interface CallMthInterface {

MethodInfo getCallMth();

@Nullable
RegisterArg getInstanceArg();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ public boolean isSame(InsnNode obj) {

@Override
public String toString() {
return super.toString() + ' ' + clsType;
return super.toString() + ' ' + clsType + ".class";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;

public class ConstructorInsn extends InsnNode implements CallMthInterface {
public final class ConstructorInsn extends InsnNode implements CallMthInterface {

private final MethodInfo callMth;
private final CallType callType;
Expand Down
41 changes: 41 additions & 0 deletions jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jadx.core.dex.nodes;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jetbrains.annotations.NotNull;
Expand All @@ -15,6 +16,7 @@
import jadx.api.ResourcesLoader;
import jadx.core.Jadx;
import jadx.core.clsp.ClspGraph;
import jadx.core.clsp.NClass;
import jadx.core.clsp.NMethod;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.ConstStorage;
Expand Down Expand Up @@ -247,6 +249,44 @@ public ArgType getMethodGenericReturnType(MethodInfo callMth) {
return null;
}

public List<ArgType> getMethodArgTypes(MethodInfo callMth) {
MethodNode methodNode = deepResolveMethod(callMth);
if (methodNode != null) {
return methodNode.getArgTypes();
}
NMethod methodDetails = clsp.getMethodDetails(callMth);
if (methodDetails != null && methodDetails.getGenericArgs() != null) {
List<ArgType> argTypes = callMth.getArgumentsTypes();
int argsCount = argTypes.size();
List<ArgType> list = new ArrayList<>(argsCount);
for (int i = 0; i < argsCount; i++) {
ArgType genericArgType = methodDetails.getGenericArg(i);
if (genericArgType != null) {
list.add(genericArgType);
} else {
list.add(argTypes.get(i));
}
}
return list;
}
return Collections.emptyList();
}

@NotNull
public List<GenericInfo> getClassGenerics(ArgType type) {
ClassNode classNode = resolveClass(ClassInfo.fromType(this, type));
if (classNode != null) {
classNode.loadAndProcess();
return classNode.getGenerics();
}
NClass clsDetails = getClsp().getClsDetails(type);
if (clsDetails == null || clsDetails.getGenerics().isEmpty()) {
return Collections.emptyList();
}
List<GenericInfo> generics = clsDetails.getGenerics();
return generics == null ? Collections.emptyList() : generics;
}

public List<DexNode> getDexNodes() {
return dexNodes;
}
Expand Down Expand Up @@ -295,4 +335,5 @@ public TypeUpdate getTypeUpdate() {
public ICodeCache getCodeCache() {
return codeCache;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
Expand Down Expand Up @@ -392,7 +393,10 @@ private static boolean inlinePhiInsn(MethodNode mth, BlockNode block, PhiInsn ph
}
inlArg.getSVar().use(inlArg);
inlArg.setName(useArg.getName());
inlArg.setType(useArg.getType());
ArgType type = useArg.getImmutableType();
if (type != null) {
inlArg.setType(type);
}
}
if (block.contains(AType.EXC_HANDLER)) {
// don't inline into exception handler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.TypeUtils;

/**
* Special dynamic bound for invoke with generics.
Expand Down Expand Up @@ -37,7 +38,7 @@ public ArgType getType() {
}

private ArgType getReturnType(ArgType instanceType) {
ArgType resultGeneric = TypeUpdate.getResultGeneric(root, instanceType, genericReturnType);
ArgType resultGeneric = TypeUtils.replaceClassGenerics(root, instanceType, genericReturnType);
if (resultGeneric != null) {
return resultGeneric;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package jadx.core.dex.visitors.typeinference;

import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -13,7 +12,6 @@
import org.slf4j.LoggerFactory;

import jadx.core.Consts;
import jadx.core.clsp.NClass;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeNode;
Expand All @@ -22,9 +20,9 @@
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.GenericInfo;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.TypeUtils;
import jadx.core.utils.exceptions.JadxOverflowException;
import jadx.core.utils.exceptions.JadxRuntimeException;

Expand Down Expand Up @@ -211,14 +209,6 @@ private boolean inBounds(@Nullable TypeUpdateInfo updateInfo, Set<ITypeBound> bo
return true;
}

private boolean inBounds(TypeUpdateInfo updateInfo, InsnArg arg, ArgType candidateType) {
if (arg.isRegister()) {
TypeInfo typeInfo = ((RegisterArg) arg).getSVar().getTypeInfo();
return inBounds(updateInfo, typeInfo.getBounds(), candidateType);
}
return arg.getType().equals(candidateType);
}

private boolean checkBound(ArgType candidateType, ITypeBound bound, ArgType boundType) {
TypeCompareEnum compareResult = comparator.compareTypes(candidateType, boundType);
switch (compareResult) {
Expand Down Expand Up @@ -303,7 +293,7 @@ private TypeUpdateResult invokeListener(TypeUpdateInfo updateInfo, InsnNode insn
if (returnType == null) {
return SAME;
}
ArgType resultGeneric = getResultGeneric(root, candidateType, returnType);
ArgType resultGeneric = TypeUtils.replaceClassGenerics(root, candidateType, returnType);
if (resultGeneric == null) {
return SAME;
}
Expand All @@ -313,54 +303,6 @@ private TypeUpdateResult invokeListener(TypeUpdateInfo updateInfo, InsnNode insn
return SAME;
}

@Nullable
public static ArgType getResultGeneric(RootNode root, ArgType instanceType, ArgType genericRetType) {
if (genericRetType == null) {
return null;
}
if (instanceType.isGeneric()) {
NClass clsDetails = root.getClsp().getClsDetails(instanceType);
if (clsDetails == null || clsDetails.getGenerics().isEmpty()) {
return null;
}
List<GenericInfo> generics = clsDetails.getGenerics();
ArgType[] actualTypes = instanceType.getGenericTypes();
if (generics.size() != actualTypes.length) {
return null;
}
Map<ArgType, ArgType> replaceMap = new LinkedHashMap<>();
for (int i = 0; i < actualTypes.length; i++) {
ArgType actualType = actualTypes[i];
ArgType genericType = generics.get(i).getGenericType();
replaceMap.put(genericType, actualType);
}
return replaceGenericTypes(genericRetType, replaceMap);
}
return null;
}

private static ArgType replaceGenericTypes(ArgType replaceType, Map<ArgType, ArgType> replaceMap) {
if (replaceType.isGenericType()) {
return replaceMap.get(replaceType);
}

ArgType[] genericTypes = replaceType.getGenericTypes();
if (replaceType.isGeneric() && genericTypes != null && genericTypes.length != 0) {
int size = genericTypes.length;
ArgType[] newTypes = new ArgType[size];
for (int i = 0; i < size; i++) {
ArgType genericType = genericTypes[i];
ArgType type = replaceGenericTypes(genericType, replaceMap);
if (type == null) {
type = genericType;
}
newTypes[i] = type;
}
return ArgType.generic(replaceType.getObject(), newTypes);
}
return null;
}

private TypeUpdateResult sameFirstArgListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {
InsnArg changeArg = isAssign(insn, arg) ? insn.getArg(0) : insn.getResult();
return updateTypeChecked(updateInfo, changeArg, candidateType);
Expand Down
Loading

0 comments on commit db2b537

Please sign in to comment.