From dea7714ef3207b0f7e0e434d82998dbf20ae7e18 Mon Sep 17 00:00:00 2001 From: Skylot Date: Wed, 29 Jan 2020 16:39:38 +0300 Subject: [PATCH] feat: add methods information from standard library, improve generics and varargs restore (#836) --- jadx-core/clsp-data/android-29-clst.jar | Bin 77171 -> 124643 bytes .../src/main/java/jadx/api/JadxArgs.java | 8 + .../src/main/java/jadx/api/JavaMethod.java | 6 - jadx-core/src/main/java/jadx/core/Jadx.java | 4 + .../src/main/java/jadx/core/clsp/ClsSet.java | 509 ++++++++++-------- .../main/java/jadx/core/clsp/ClspClass.java | 101 ++++ .../main/java/jadx/core/clsp/ClspGraph.java | 93 +++- .../main/java/jadx/core/clsp/ClspMethod.java | 122 +++++ .../java/jadx/core/clsp/ConvertToClsSet.java | 9 +- .../src/main/java/jadx/core/clsp/NClass.java | 96 ---- .../src/main/java/jadx/core/clsp/NMethod.java | 92 ---- .../jadx/core/clsp/SimpleMethodDetails.java | 53 ++ .../java/jadx/core/codegen/AnnotationGen.java | 8 +- .../main/java/jadx/core/codegen/ClassGen.java | 10 +- .../main/java/jadx/core/codegen/InsnGen.java | 110 +--- .../java/jadx/core/codegen/MethodGen.java | 6 +- .../java/jadx/core/dex/attributes/AFlag.java | 1 + .../java/jadx/core/dex/attributes/AType.java | 2 + .../core/dex/attributes/AttributeStorage.java | 10 +- .../jadx/core/dex/attributes/IAttribute.java | 4 + .../java/jadx/core/dex/info/ClassInfo.java | 2 +- .../java/jadx/core/dex/info/InfoStorage.java | 26 +- .../java/jadx/core/dex/info/MethodInfo.java | 102 ++-- .../core/dex/instructions/BaseInvokeNode.java | 25 + .../dex/instructions/CallMthInterface.java | 19 - .../core/dex/instructions/InvokeNode.java | 16 +- .../core/dex/instructions/args/ArgType.java | 70 ++- .../instructions/mods/ConstructorInsn.java | 10 +- .../java/jadx/core/dex/nodes/ClassNode.java | 10 +- .../java/jadx/core/dex/nodes/GenericInfo.java | 46 -- .../core/dex/nodes/GenericTypeParameter.java | 55 ++ .../jadx/core/dex/nodes/IMethodDetails.java | 52 ++ .../java/jadx/core/dex/nodes/InsnNode.java | 2 +- .../java/jadx/core/dex/nodes/MethodNode.java | 77 ++- .../java/jadx/core/dex/nodes/RootNode.java | 91 ++-- .../dex/nodes/parser/SignatureParser.java | 55 +- .../core/dex/nodes/utils/MethodUtils.java | 125 +++++ .../jadx/core/dex/nodes/utils/TypeUtils.java | 142 +++++ .../dex/visitors/AttachMethodDetails.java | 50 ++ .../core/dex/visitors/ConstInlineVisitor.java | 6 +- .../core/dex/visitors/DeboxingVisitor.java | 2 +- .../dex/visitors/MethodInvokeVisitor.java | 372 +++++++++++++ .../jadx/core/dex/visitors/ModVisitor.java | 2 +- .../core/dex/visitors/SimplifyVisitor.java | 5 +- .../visitors/blocksmaker/BlockProcessor.java | 3 +- .../debuginfo/DebugInfoApplyVisitor.java | 12 +- .../core/dex/visitors/debuginfo/LocalVar.java | 3 +- .../dex/visitors/regions/IfRegionVisitor.java | 5 +- .../dex/visitors/regions/ReturnVisitor.java | 3 +- .../core/dex/visitors/regions/TernaryMod.java | 3 +- .../typeinference/TypeBoundInvokeAssign.java | 3 +- .../visitors/typeinference/TypeCompare.java | 36 +- .../typeinference/TypeCompareEnum.java | 8 + .../typeinference/TypeInferenceVisitor.java | 26 +- .../visitors/typeinference/TypeUpdate.java | 14 +- .../main/java/jadx/core/utils/TypeUtils.java | 119 ---- .../src/main/java/jadx/core/utils/Utils.java | 7 + .../dex/instructions/args/ArgTypeTest.java | 4 +- .../typeinference/TypeCompareTest.java | 51 +- .../java/jadx/core/utils/TypeUtilsTest.java | 12 +- .../tests/functional/JadxClasspathTest.java | 27 +- .../tests/functional/SignatureParserTest.java | 10 +- .../integration/conditions/TestElseIf.java | 4 +- .../invoke/TestCastInOverloadedInvoke.java | 17 +- .../invoke/TestCastInOverloadedInvoke3.java | 41 ++ .../invoke/TestHierarchyOverloadedInvoke.java | 100 ++++ .../invoke/TestSuperInvokeWithGenerics.java | 4 +- .../integration/others/TestDeboxing2.java | 2 +- .../integration/others/TestLoopInTry2.java | 2 +- .../others/TestStringBuilderElimination2.java | 4 +- .../trycatch/TestTryCatchFinally2.java | 21 +- .../integration/types/TestGenerics2.java | 1 + .../integration/types/TestGenerics4.java | 2 +- .../integration/types/TestTypeResolver12.java | 4 +- .../integration/types/TestTypeResolver13.java | 41 ++ 75 files changed, 2174 insertions(+), 1021 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/clsp/ClspClass.java create mode 100644 jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java delete mode 100644 jadx-core/src/main/java/jadx/core/clsp/NClass.java delete mode 100644 jadx-core/src/main/java/jadx/core/clsp/NMethod.java create mode 100644 jadx-core/src/main/java/jadx/core/clsp/SimpleMethodDetails.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/BaseInvokeNode.java delete mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java delete mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/GenericInfo.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/GenericTypeParameter.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/IMethodDetails.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/AttachMethodDetails.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java delete mode 100644 jadx-core/src/main/java/jadx/core/utils/TypeUtils.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke3.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver13.java diff --git a/jadx-core/clsp-data/android-29-clst.jar b/jadx-core/clsp-data/android-29-clst.jar index 5a4e2de981dcce9a10851669e7dcac889c216adf..cc5c4432d455ea5412e694d5e47a93e06e0b93e0 100644 GIT binary patch literal 124643 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∋awSGz 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

A-QU!`k8+68?iu6r$5o+F(&vG}DIqQH_DH*R@U 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 renameFlags) { + this.renameFlags = renameFlags; + } + + public Set getRenameFlags() { + return renameFlags; + } + public OutputFormatEnum getOutputFormat() { return outputFormat; } diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index 53a51e94b..82b5ac492 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -47,18 +47,12 @@ public final class JavaMethod implements JavaNode { return Collections.emptyList(); } List arguments = mth.getArgTypes(); - if (arguments == null) { - arguments = infoArgTypes; - } return Utils.collectionMap(arguments, type -> ArgType.tryToResolveClassAlias(mth.dex(), type)); } public ArgType getReturnType() { ArgType retType = mth.getReturnType(); - if (retType == null) { - retType = mth.getMethodInfo().getReturnType(); - } return ArgType.tryToResolveClassAlias(mth.dex(), retType); } diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 02fd8fe24..bf0d8a095 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.JadxArgs; +import jadx.core.dex.visitors.AttachMethodDetails; import jadx.core.dex.visitors.ClassModifier; import jadx.core.dex.visitors.ConstInlineVisitor; import jadx.core.dex.visitors.ConstructorVisitor; @@ -25,6 +26,7 @@ import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.InitCodeVariables; import jadx.core.dex.visitors.MarkFinallyVisitor; import jadx.core.dex.visitors.MethodInlineVisitor; +import jadx.core.dex.visitors.MethodInvokeVisitor; import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.PrepareForCodeGen; import jadx.core.dex.visitors.ProcessAnonymous; @@ -83,6 +85,7 @@ public class Jadx { passes.add(new InitCodeVariables()); passes.add(new MarkFinallyVisitor()); passes.add(new ConstInlineVisitor()); + passes.add(new AttachMethodDetails()); passes.add(new TypeInferenceVisitor()); if (args.isDebugInfo()) { passes.add(new DebugInfoApplyVisitor()); @@ -102,6 +105,7 @@ public class Jadx { passes.add(new CleanRegions()); passes.add(new CodeShrinkVisitor()); + passes.add(new MethodInvokeVisitor()); passes.add(new SimplifyVisitor()); passes.add(new CheckRegions()); diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java index 426b8a8a7..565a11b9b 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -1,5 +1,6 @@ package jadx.core.clsp; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -12,21 +13,26 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.dex.info.AccessInfo; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; -import jadx.core.dex.nodes.GenericInfo; +import jadx.core.dex.nodes.GenericTypeParameter; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.DecodeException; @@ -34,6 +40,9 @@ import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; import jadx.core.utils.files.ZipSecurity; +import static jadx.core.utils.Utils.isEmpty; +import static jadx.core.utils.Utils.notEmpty; + /** * Classes list for import into classpath graph */ @@ -45,123 +54,132 @@ public class ClsSet { private static final String CLST_PKG_PATH = ClsSet.class.getPackage().getName().replace('.', '/'); private static final String JADX_CLS_SET_HEADER = "jadx-cst"; - private static final int VERSION = 2; + private static final int VERSION = 3; private static final String STRING_CHARSET = "US-ASCII"; - private static final NClass[] EMPTY_NCLASS_ARRAY = new NClass[0]; + private static final ArgType[] EMPTY_ARGTYPE_ARRAY = new ArgType[0]; - private enum TypeEnum { - WILDCARD, GENERIC, GENERIC_TYPE, OBJECT, ARRAY, PRIMITIVE + private final RootNode root; + + public ClsSet(RootNode root) { + this.root = root; } - private NClass[] classes; + private enum TypeEnum { + WILDCARD, + GENERIC, + GENERIC_TYPE_VARIABLE, + OUTER_GENERIC, + OBJECT, + ARRAY, + PRIMITIVE + } + + private ClspClass[] classes; public void loadFromClstFile() throws IOException, DecodeException { + long startTime = System.currentTimeMillis(); try (InputStream input = getClass().getResourceAsStream(CLST_FILENAME)) { if (input == null) { throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME); } load(input); } + if (LOG.isDebugEnabled()) { + long time = System.currentTimeMillis() - startTime; + int methodsCount = Arrays.stream(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum(); + LOG.debug("Load class set in {}ms, classes: {}, methods: {}", time, classes.length, methodsCount); + } } public void loadFrom(RootNode root) { List list = root.getClasses(true); - Map names = new HashMap<>(list.size()); + Map names = new HashMap<>(list.size()); int k = 0; for (ClassNode cls : list) { - String clsRawName = cls.getRawName(); - if (cls.getAccessFlags().isPublic()) { - cls.load(); - NClass nClass = new NClass(clsRawName, k); - if (names.put(clsRawName, nClass) != null) { - throw new JadxRuntimeException("Duplicate class: " + clsRawName); - } - k++; - nClass.setGenerics(cls.getGenerics()); - nClass.setMethods(getMethodsDetails(cls)); - } else { - names.put(clsRawName, null); + ArgType clsType = cls.getClassInfo().getType(); + String clsRawName = clsType.getObject(); + cls.load(); + ClspClass nClass = new ClspClass(clsType, k); + if (names.put(clsRawName, nClass) != null) { + throw new JadxRuntimeException("Duplicate class: " + clsRawName); } + k++; + nClass.setTypeParameters(cls.getGenericTypeParameters()); + nClass.setMethods(getMethodsDetails(cls)); } - classes = new NClass[k]; + classes = new ClspClass[k]; k = 0; for (ClassNode cls : list) { - if (cls.getAccessFlags().isPublic()) { - NClass nClass = getCls(cls.getRawName(), names); - if (nClass == null) { - throw new JadxRuntimeException("Missing class: " + cls); - } - nClass.setParents(makeParentsArray(cls, names)); - classes[k] = nClass; - k++; + ClspClass nClass = getCls(cls, names); + if (nClass == null) { + throw new JadxRuntimeException("Missing class: " + cls); } + nClass.setParents(makeParentsArray(cls)); + classes[k] = nClass; + k++; } } - private List getMethodsDetails(ClassNode cls) { - List methods = new ArrayList<>(); - for (MethodNode m : cls.getMethods()) { - AccessInfo accessFlags = m.getAccessFlags(); - if (accessFlags.isPublic() || accessFlags.isProtected()) { - processMethodDetails(methods, m, accessFlags); - } + private List getMethodsDetails(ClassNode cls) { + List methodsList = cls.getMethods(); + List methods = new ArrayList<>(methodsList.size()); + for (MethodNode mth : methodsList) { + processMethodDetails(mth, methods); } return methods; } - private void processMethodDetails(List methods, MethodNode mth, AccessInfo accessFlags) { - List args = mth.getArgTypes(); - boolean genericArg = false; - ArgType[] genericArgs; - if (args.isEmpty()) { - genericArgs = null; - } else { - int argsCount = args.size(); - genericArgs = new ArgType[argsCount]; - for (int i = 0; i < argsCount; i++) { - ArgType argType = args.get(i); - if (argType.isGeneric() || argType.isGenericType()) { - genericArgs[i] = argType; - genericArg = true; - } - } - } - ArgType retType = mth.getReturnType(); - if (!retType.isGeneric() && !retType.isGenericType()) { - retType = null; + private void processMethodDetails(MethodNode mth, List methods) { + AccessInfo accessFlags = mth.getAccessFlags(); + if (accessFlags.isPrivate()) { + return; } + ArgType genericRetType = mth.getReturnType(); boolean varArgs = accessFlags.isVarArgs(); - if (genericArg || retType != null || varArgs) { - methods.add(new NMethod(mth.getMethodInfo().getShortId(), genericArgs, retType, varArgs)); + List throwList = mth.getThrows(); + List typeParameters = mth.getTypeParameters(); + // add only methods with additional info + if (varArgs + || notEmpty(throwList) + || notEmpty(typeParameters) + || genericRetType.containsGeneric() + || mth.containsGenericArgs() + || mth.isArgsOverloaded()) { + ClspMethod clspMethod = new ClspMethod(mth.getMethodInfo(), + mth.getArgTypes(), genericRetType, + typeParameters, varArgs, throwList); + methods.add(clspMethod); } } - public static NClass[] makeParentsArray(ClassNode cls, Map names) { - List parents = new ArrayList<>(1 + cls.getInterfaces().size()); + public static ArgType[] makeParentsArray(ClassNode cls) { ArgType superClass = cls.getSuperClass(); - if (superClass != null) { - NClass c = getCls(superClass.getObject(), names); - if (c != null) { - parents.add(c); - } + if (superClass == null) { + // cls is java.lang.Object + return EMPTY_ARGTYPE_ARRAY; } + ArgType[] parents = new ArgType[1 + cls.getInterfaces().size()]; + parents[0] = superClass; + int k = 1; for (ArgType iface : cls.getInterfaces()) { - NClass c = getCls(iface.getObject(), names); - if (c != null) { - parents.add(c); - } + parents[k] = iface; + k++; } - int size = parents.size(); - if (size == 0) { - return EMPTY_NCLASS_ARRAY; - } - return parents.toArray(new NClass[size]); + return parents; } - private static NClass getCls(String fullName, Map names) { - NClass cls = names.get(fullName); + private static ClspClass getCls(ClassNode cls, Map names) { + return getCls(cls.getRawName(), names); + } + + private static ClspClass getCls(ArgType clsType, Map names) { + return getCls(clsType.getObject(), names); + } + + private static ClspClass getCls(String fullName, Map names) { + ClspClass cls = names.get(fullName); if (cls == null) { LOG.debug("Class not found: {}", fullName); } @@ -182,16 +200,25 @@ public class ClsSet { try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(path)); ZipInputStream in = new ZipInputStream(Files.newInputStream(temp))) { String clst = CLST_PKG_PATH + '/' + CLST_FILENAME; - out.putNextEntry(new ZipEntry(clst)); - save(out); + boolean clstReplaced = false; ZipEntry entry = in.getNextEntry(); while (entry != null) { - if (!entry.getName().equals(clst)) { - out.putNextEntry(new ZipEntry(entry.getName())); + String entryName = entry.getName(); + ZipEntry copyEntry = new ZipEntry(entryName); + copyEntry.setLastModifiedTime(entry.getLastModifiedTime()); // preserve modified time + out.putNextEntry(copyEntry); + if (entryName.equals(clst)) { + save(out); + clstReplaced = true; + } else { FileUtils.copyStream(in, out); } entry = in.getNextEntry(); } + if (!clstReplaced) { + out.putNextEntry(new ZipEntry(clst)); + save(out); + } } } else { throw new JadxRuntimeException("Unknown file format: " + outputName); @@ -203,76 +230,90 @@ public class ClsSet { out.writeBytes(JADX_CLS_SET_HEADER); out.writeByte(VERSION); - LOG.info("Classes count: {}", classes.length); - Map names = new HashMap<>(classes.length); + Map names = new HashMap<>(classes.length); out.writeInt(classes.length); - for (NClass cls : classes) { - writeString(out, cls.getName()); - names.put(cls.getName(), cls); + for (ClspClass cls : classes) { + String clsName = cls.getName(); + writeString(out, clsName); + names.put(clsName, cls); } - for (NClass cls : classes) { - NClass[] parents = cls.getParents(); - out.writeByte(parents.length); - for (NClass parent : parents) { - out.writeInt(parent.getId()); - } - writeGenerics(out, cls, names); - List methods = cls.getMethodsList(); - out.writeByte(methods.size()); - for (NMethod method : methods) { + for (ClspClass cls : classes) { + writeArgTypesArray(out, cls.getParents(), names); + writeGenericTypeParameters(out, cls.getTypeParameters(), names); + List methods = cls.getSortedMethodsList(); + out.writeShort(methods.size()); + for (ClspMethod method : methods) { writeMethod(out, method, names); } } + int methodsCount = Arrays.stream(classes).mapToInt(c -> c.getMethodsMap().size()).sum(); + LOG.info("Classes: {}, methods: {}, file size: {}B", classes.length, methodsCount, out.size()); } - private static void writeGenerics(DataOutputStream out, NClass cls, Map names) throws IOException { - List genericsList = cls.getGenerics(); - out.writeByte(genericsList.size()); - for (GenericInfo genericInfo : genericsList) { - writeArgType(out, genericInfo.getGenericType(), names); - List extendsList = genericInfo.getExtendsList(); - out.writeByte(extendsList.size()); - for (ArgType type : extendsList) { - writeArgType(out, type, names); - } - - } - } - - private static void writeMethod(DataOutputStream out, NMethod method, Map names) throws IOException { - writeLongString(out, method.getShortId()); - - ArgType[] argTypes = method.getGenericArgs(); - if (argTypes == null) { + private static void writeGenericTypeParameters(DataOutputStream out, + List typeParameters, + Map names) throws IOException { + if (isEmpty(typeParameters)) { out.writeByte(0); } else { - int argCount = 0; - for (ArgType arg : argTypes) { - if (arg != null) { - argCount++; - } - } - out.writeByte(argCount); - // last argument first - for (int i = argTypes.length - 1; i >= 0; i--) { - ArgType argType = argTypes[i]; - if (argType != null) { - out.writeByte(i); - writeArgType(out, argType, names); - } + writeUnsignedByte(out, typeParameters.size()); + for (GenericTypeParameter typeParameter : typeParameters) { + writeArgType(out, typeParameter.getTypeVariable(), names); + writeArgTypesList(out, typeParameter.getExtendsList(), names); } } - if (method.getReturnType() == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - writeArgType(out, method.getReturnType(), names); - } - out.writeBoolean(method.isVarArgs()); } - private static void writeArgType(DataOutputStream out, ArgType argType, Map names) throws IOException { - if (argType.getWildcardType() != null) { + private static void writeMethod(DataOutputStream out, ClspMethod method, Map names) throws IOException { + MethodInfo methodInfo = method.getMethodInfo(); + writeString(out, methodInfo.getName()); + writeArgTypesList(out, methodInfo.getArgumentsTypes(), names); + writeArgType(out, methodInfo.getReturnType(), names); + + writeArgTypesList(out, method.containsGenericArgs() ? method.getArgTypes() : Collections.emptyList(), names); + writeArgType(out, method.getReturnType(), names); + writeGenericTypeParameters(out, method.getTypeParameters(), names); + out.writeBoolean(method.isVarArg()); + writeArgTypesList(out, method.getThrows(), names); + } + + private static void writeArgTypesList(DataOutputStream out, List list, Map names) throws IOException { + int size = list.size(); + writeUnsignedByte(out, size); + if (size != 0) { + for (ArgType type : list) { + writeArgType(out, type, names); + } + } + } + + private static void writeArgTypesArray(DataOutputStream out, @Nullable ArgType[] arr, Map names) throws IOException { + if (arr == null) { + out.writeByte(-1); + return; + } + int size = arr.length; + out.writeByte(size); + if (size != 0) { + for (ArgType type : arr) { + writeArgType(out, type, names); + } + } + } + + private static void writeArgType(DataOutputStream out, ArgType argType, Map names) throws IOException { + if (argType == null) { + out.writeByte(-1); + return; + } + if (argType.isPrimitive()) { + out.writeByte(TypeEnum.PRIMITIVE.ordinal()); + out.writeByte(argType.getPrimitiveType().getShortName().charAt(0)); + } else if (argType.getOuterType() != null) { + out.writeByte(TypeEnum.OUTER_GENERIC.ordinal()); + writeArgType(out, argType.getOuterType(), names); + writeArgType(out, argType.getInnerType(), names); + } else if (argType.getWildcardType() != null) { out.writeByte(TypeEnum.WILDCARD.ordinal()); ArgType.WildcardBound bound = argType.getWildcardBound(); out.writeByte(bound.getNum()); @@ -281,28 +322,18 @@ public class ClsSet { } } else if (argType.isGeneric()) { out.writeByte(TypeEnum.GENERIC.ordinal()); - out.writeInt(names.get(argType.getObject()).getId()); + out.writeInt(getCls(argType, names).getId()); ArgType[] types = argType.getGenericTypes(); - if (types == null) { - out.writeByte(0); - } else { - out.writeByte(types.length); - for (ArgType type : types) { - writeArgType(out, type, names); - } - } + writeArgTypesArray(out, types, names); } else if (argType.isGenericType()) { - out.writeByte(TypeEnum.GENERIC_TYPE.ordinal()); + out.writeByte(TypeEnum.GENERIC_TYPE_VARIABLE.ordinal()); writeString(out, argType.getObject()); } else if (argType.isObject()) { out.writeByte(TypeEnum.OBJECT.ordinal()); - out.writeInt(names.get(argType.getObject()).getId()); + out.writeInt(getCls(argType, names).getId()); } else if (argType.isArray()) { out.writeByte(TypeEnum.ARRAY.ordinal()); writeArgType(out, argType.getArrayElement(), names); - } else if (argType.isPrimitive()) { - out.writeByte(TypeEnum.PRIMITIVE.ordinal()); - out.writeByte(argType.getPrimitiveType().getShortName().charAt(0)); } else { throw new JadxRuntimeException("Cannot save type: " + argType); } @@ -330,7 +361,7 @@ public class ClsSet { } private void load(InputStream input) throws IOException, DecodeException { - try (DataInputStream in = new DataInputStream(input)) { + try (DataInputStream in = new DataInputStream(new BufferedInputStream(input))) { byte[] header = new byte[JADX_CLS_SET_HEADER.length()]; int readHeaderLength = in.read(header); int version = in.readByte(); @@ -339,87 +370,119 @@ public class ClsSet { || version != VERSION) { throw new DecodeException("Wrong jadx class set header"); } - int count = in.readInt(); - classes = new NClass[count]; - for (int i = 0; i < count; i++) { + int clsCount = in.readInt(); + classes = new ClspClass[clsCount]; + for (int i = 0; i < clsCount; i++) { String name = readString(in); - classes[i] = new NClass(name, i); + classes[i] = new ClspClass(ArgType.object(name), i); } - for (int i = 0; i < count; i++) { - int pCount = in.readByte(); - NClass[] parents = new NClass[pCount]; - for (int j = 0; j < pCount; j++) { - parents[j] = classes[in.readInt()]; - } - NClass nClass = classes[i]; - nClass.setParents(parents); - nClass.setGenerics(readGenerics(in)); - nClass.setMethods(readClsMethods(in)); + for (int i = 0; i < clsCount; i++) { + ClspClass nClass = classes[i]; + ClassInfo clsInfo = ClassInfo.fromType(root, nClass.getClsType()); + nClass.setParents(readArgTypesArray(in)); + nClass.setTypeParameters(readGenericTypeParameters(in)); + nClass.setMethods(readClsMethods(in, clsInfo)); } } } - private List readGenerics(DataInputStream in) throws IOException { - int count = in.readByte(); + private List readGenericTypeParameters(DataInputStream in) throws IOException { + int count = readUnsignedByte(in); if (count == 0) { return Collections.emptyList(); } - List list = new ArrayList<>(count); + List list = new ArrayList<>(count); for (int i = 0; i < count; i++) { - ArgType genericType = readArgType(in); - List extendsList; - byte extCount = in.readByte(); - if (extCount == 0) { - extendsList = Collections.emptyList(); - } else { - extendsList = new ArrayList<>(extCount); - for (int j = 0; j < extCount; j++) { - extendsList.add(readArgType(in)); - } - } - list.add(new GenericInfo(genericType, extendsList)); + ArgType typeVariable = readArgType(in); + List extendsList = readArgTypesList(in); + list.add(new GenericTypeParameter(typeVariable, extendsList)); } return list; } - private List readClsMethods(DataInputStream in) throws IOException { - int mCount = in.readByte(); - List methods = new ArrayList<>(mCount); + private List readClsMethods(DataInputStream in, ClassInfo clsInfo) throws IOException { + int mCount = in.readShort(); + List methods = new ArrayList<>(mCount); for (int j = 0; j < mCount; j++) { - methods.add(readMethod(in)); + methods.add(readMethod(in, clsInfo)); } return methods; } - private NMethod readMethod(DataInputStream in) throws IOException { - String shortId = readLongString(in); - int argCount = in.readByte(); - ArgType[] argTypes = null; - for (int i = 0; i < argCount; i++) { - int index = in.readByte(); - ArgType argType = readArgType(in); - if (argTypes == null) { - argTypes = new ArgType[index + 1]; - } - argTypes[index] = argType; + private ClspMethod readMethod(DataInputStream in, ClassInfo clsInfo) throws IOException { + String name = readString(in); + List argTypes = readArgTypesList(in); + ArgType retType = readArgType(in); + List genericArgTypes = readArgTypesList(in); + if (genericArgTypes.isEmpty() || Objects.equals(genericArgTypes, argTypes)) { + genericArgTypes = argTypes; } - ArgType retType = in.readBoolean() ? readArgType(in) : null; + ArgType genericRetType = readArgType(in); + if (Objects.equals(genericRetType, retType)) { + genericRetType = retType; + } + List typeParameters = readGenericTypeParameters(in); boolean varArgs = in.readBoolean(); - return new NMethod(shortId, argTypes, retType, varArgs); + List throwList = readArgTypesList(in); + MethodInfo methodInfo = MethodInfo.fromDetails(root, clsInfo, name, argTypes, retType); + return new ClspMethod(methodInfo, + genericArgTypes, genericRetType, + typeParameters, varArgs, throwList); + } + + private List readArgTypesList(DataInputStream in) throws IOException { + int count = in.readByte(); + if (count == 0) { + return Collections.emptyList(); + } + List list = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + list.add(readArgType(in)); + } + return list; + } + + @Nullable + private ArgType[] readArgTypesArray(DataInputStream in) throws IOException { + int count = in.readByte(); + if (count == -1) { + return null; + } + if (count == 0) { + return EMPTY_ARGTYPE_ARRAY; + } + ArgType[] arr = new ArgType[count]; + for (int i = 0; i < count; i++) { + arr[i] = readArgType(in); + } + return arr; } private ArgType readArgType(DataInputStream in) throws IOException { int ordinal = in.readByte(); + if (ordinal == -1) { + return null; + } + if (ordinal >= TypeEnum.values().length) { + throw new JadxRuntimeException("Incorrect ordinal for type enum: " + ordinal); + } switch (TypeEnum.values()[ordinal]) { case WILDCARD: - int bounds = in.readByte(); - return bounds == 0 - ? ArgType.wildcard() - : ArgType.wildcard(readArgType(in), ArgType.WildcardBound.getByNum(bounds)); + ArgType.WildcardBound bound = ArgType.WildcardBound.getByNum(in.readByte()); + if (bound == ArgType.WildcardBound.UNBOUND) { + return ArgType.WILDCARD; + } + ArgType objType = readArgType(in); + return ArgType.wildcard(objType, bound); + + case OUTER_GENERIC: + ArgType outerType = readArgType(in); + ArgType innerType = readArgType(in); + return ArgType.outerGeneric(outerType, innerType); case GENERIC: - String obj = classes[in.readInt()].getName(); - int typeLength = in.readByte(); + ArgType clsType = classes[in.readInt()].getClsType(); + int typeLength = readUnsignedByte(in); ArgType[] generics; if (typeLength == 0) { generics = null; @@ -429,13 +492,13 @@ public class ClsSet { generics[i] = readArgType(in); } } - return ArgType.generic(obj, generics); + return ArgType.generic(clsType, generics); - case GENERIC_TYPE: + case GENERIC_TYPE_VARIABLE: return ArgType.genericType(readString(in)); case OBJECT: - return ArgType.object(classes[in.readInt()].getName()); + return classes[in.readInt()].getClsType(); case ARRAY: return ArgType.array(readArgType(in)); @@ -451,23 +514,16 @@ public class ClsSet { private static void writeString(DataOutputStream out, String name) throws IOException { byte[] bytes = name.getBytes(STRING_CHARSET); - out.writeByte(bytes.length); - out.write(bytes); - } - - private static void writeLongString(DataOutputStream out, String name) throws IOException { - byte[] bytes = name.getBytes(STRING_CHARSET); - out.writeShort(bytes.length); + int len = bytes.length; + if (len >= 0xFF) { + throw new JadxRuntimeException("String is too long: " + name); + } + writeUnsignedByte(out, bytes.length); out.write(bytes); } private static String readString(DataInputStream in) throws IOException { - int len = in.readByte(); - return readString(in, len); - } - - private static String readLongString(DataInputStream in) throws IOException { - int len = in.readShort(); + int len = readUnsignedByte(in); return readString(in, len); } @@ -485,12 +541,23 @@ public class ClsSet { return new String(bytes, STRING_CHARSET); } + private static void writeUnsignedByte(DataOutputStream out, int value) throws IOException { + if (value < 0 || value >= 0xFF) { + throw new JadxRuntimeException("Unsigned byte value is too big: " + value); + } + out.writeByte(value); + } + + private static int readUnsignedByte(DataInputStream in) throws IOException { + return ((int) in.readByte()) & 0xFF; + } + public int getClassesCount() { return classes.length; } - public void addToMap(Map nameMap) { - for (NClass cls : classes) { + public void addToMap(Map nameMap) { + for (ClspClass cls : classes) { nameMap.put(cls.getName(), cls); } } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspClass.java b/jadx-core/src/main/java/jadx/core/clsp/ClspClass.java new file mode 100644 index 000000000..97a01ccb3 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspClass.java @@ -0,0 +1,101 @@ +package jadx.core.clsp; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.nodes.GenericTypeParameter; + +/** + * Class node in classpath graph + */ +public class ClspClass { + + private final ArgType clsType; + private final int id; + private ArgType[] parents; + private Map methodsMap = Collections.emptyMap(); + private List typeParameters = Collections.emptyList(); + + public ClspClass(ArgType clsType, int id) { + this.clsType = clsType; + this.id = id; + } + + public String getName() { + return clsType.getObject(); + } + + public ArgType getClsType() { + return clsType; + } + + public int getId() { + return id; + } + + public ArgType[] getParents() { + return parents; + } + + public void setParents(ArgType[] parents) { + this.parents = parents; + } + + public Map getMethodsMap() { + return methodsMap; + } + + public List getSortedMethodsList() { + List list = new ArrayList<>(methodsMap.size()); + list.addAll(methodsMap.values()); + Collections.sort(list); + return list; + } + + public void setMethodsMap(Map methodsMap) { + this.methodsMap = Objects.requireNonNull(methodsMap); + } + + public void setMethods(List methods) { + Map map = new HashMap<>(methods.size()); + for (ClspMethod mth : methods) { + map.put(mth.getMethodInfo().getShortId(), mth); + } + setMethodsMap(map); + } + + public List getTypeParameters() { + return typeParameters; + } + + public void setTypeParameters(List typeParameters) { + this.typeParameters = typeParameters; + } + + @Override + public int hashCode() { + return clsType.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClspClass nClass = (ClspClass) o; + return clsType.equals(nClass.clsType); + } + + @Override + public String toString() { + return clsType.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java index abdfc9475..c8a7e9eaa 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java @@ -17,6 +17,8 @@ import org.slf4j.LoggerFactory; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.IMethodDetails; +import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -26,13 +28,18 @@ import jadx.core.utils.exceptions.JadxRuntimeException; public class ClspGraph { private static final Logger LOG = LoggerFactory.getLogger(ClspGraph.class); + private final RootNode root; private final Map> ancestorCache = Collections.synchronizedMap(new WeakHashMap<>()); - private Map nameMap; + private Map nameMap; private final Set missingClasses = new HashSet<>(); + public ClspGraph(RootNode rootNode) { + this.root = rootNode; + } + public void load() throws IOException, DecodeException { - ClsSet set = new ClsSet(); + ClsSet set = new ClsSet(root); set.loadFromClstFile(); addClasspath(set); } @@ -51,13 +58,13 @@ public class ClspGraph { throw new JadxRuntimeException("Classpath must be loaded first"); } int size = classes.size(); - NClass[] nClasses = new NClass[size]; + ClspClass[] nClasses = new ClspClass[size]; int k = 0; for (ClassNode cls : classes) { nClasses[k++] = addClass(cls); } for (int i = 0; i < size; i++) { - nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap)); + nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i))); } } @@ -65,24 +72,44 @@ public class ClspGraph { return nameMap.containsKey(fullName); } - public NClass getClsDetails(ArgType type) { + public ClspClass getClsDetails(ArgType type) { return nameMap.get(type.getObject()); } @Nullable - public NMethod getMethodDetails(MethodInfo methodInfo) { - NClass cls = nameMap.get(methodInfo.getDeclClass().getRawName()); + public IMethodDetails getMethodDetails(MethodInfo methodInfo) { + ClspClass cls = nameMap.get(methodInfo.getDeclClass().getRawName()); if (cls == null) { return null; } + ClspMethod clspMethod = getMethodFromClass(cls, methodInfo); + if (clspMethod != null) { + return clspMethod; + } + // deep search + for (ArgType parent : cls.getParents()) { + ClspClass clspParent = getClspClass(parent); + if (clspParent != null) { + ClspMethod methodFromParent = getMethodFromClass(clspParent, methodInfo); + if (methodFromParent != null) { + return methodFromParent; + } + } + } + // all other methods in known ClspClass are 'simple' + return new SimpleMethodDetails(methodInfo); + } + + private ClspMethod getMethodFromClass(ClspClass cls, MethodInfo methodInfo) { return cls.getMethodsMap().get(methodInfo.getShortId()); } - private NClass addClass(ClassNode cls) { - String rawName = cls.getRawName(); - NClass nClass = new NClass(rawName, -1); - nameMap.put(rawName, nClass); - return nClass; + private ClspClass addClass(ClassNode cls) { + ArgType clsType = cls.getClassInfo().getType(); + String rawName = clsType.getObject(); + ClspClass clspClass = new ClspClass(clsType, -1); + nameMap.put(rawName, clspClass); + return clspClass; } /** @@ -107,7 +134,7 @@ public class ClspGraph { if (clsName.equals(implClsName)) { return clsName; } - NClass cls = nameMap.get(implClsName); + ClspClass cls = nameMap.get(implClsName); if (cls == null) { missingClasses.add(clsName); return null; @@ -119,15 +146,18 @@ public class ClspGraph { return searchCommonParent(anc, cls); } - private String searchCommonParent(Set anc, NClass cls) { - for (NClass p : cls.getParents()) { - String name = p.getName(); + private String searchCommonParent(Set anc, ClspClass cls) { + for (ArgType p : cls.getParents()) { + String name = p.getObject(); if (anc.contains(name)) { return name; } - String r = searchCommonParent(anc, p); - if (r != null) { - return r; + ClspClass nCls = getClspClass(p); + if (nCls != null) { + String r = searchCommonParent(anc, nCls); + if (r != null) { + return r; + } } } return null; @@ -138,7 +168,7 @@ public class ClspGraph { if (result != null) { return result; } - NClass cls = nameMap.get(clsName); + ClspClass cls = nameMap.get(clsName); if (cls == null) { missingClasses.add(clsName); return Collections.emptySet(); @@ -152,15 +182,32 @@ public class ClspGraph { return result; } - private void addAncestorsNames(NClass cls, Set result) { + private void addAncestorsNames(ClspClass cls, Set result) { boolean isNew = result.add(cls.getName()); if (isNew) { - for (NClass p : cls.getParents()) { - addAncestorsNames(p, result); + for (ArgType parentType : cls.getParents()) { + if (parentType == null) { + continue; + } + ClspClass parentCls = getClspClass(parentType); + if (parentCls != null) { + addAncestorsNames(parentCls, result); + } } } } + @Nullable + private ClspClass getClspClass(ArgType clsType) { + ClspClass clspClass = nameMap.get(clsType.getObject()); + if (clspClass == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("External class not found: {}", clsType.getObject()); + } + } + return clspClass; + } + public void printMissingClasses() { int count = missingClasses.size(); if (count == 0) { diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java b/jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java new file mode 100644 index 000000000..250adeeb6 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java @@ -0,0 +1,122 @@ +package jadx.core.clsp; + +import java.util.List; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +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; +import jadx.core.utils.Utils; + +/** + * Method node in classpath graph. + */ +public class ClspMethod implements IMethodDetails, Comparable { + + private final MethodInfo methodInfo; + private final List argTypes; + private final ArgType returnType; + private final List typeParameters; + private final List throwList; + private final boolean varArg; + + public ClspMethod(MethodInfo methodInfo, + List argTypes, ArgType returnType, + List typeParameters, + boolean varArgs, List throwList) { + this.methodInfo = methodInfo; + this.argTypes = argTypes; + this.returnType = returnType; + this.typeParameters = typeParameters; + this.throwList = throwList; + this.varArg = varArgs; + } + + @Override + public MethodInfo getMethodInfo() { + return methodInfo; + } + + @Override + public ArgType getReturnType() { + return returnType; + } + + @Override + public List getArgTypes() { + return argTypes; + } + + public boolean containsGenericArgs() { + return !Objects.equals(argTypes, methodInfo.getArgumentsTypes()); + } + + public int getArgsCount() { + return argTypes.size(); + } + + @Override + public List getTypeParameters() { + return typeParameters; + } + + @Override + public List getThrows() { + return throwList; + } + + @Override + public boolean isVarArg() { + return varArg; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ClspMethod)) { + return false; + } + ClspMethod other = (ClspMethod) o; + return methodInfo.equals(other.methodInfo); + } + + @Override + public int hashCode() { + return methodInfo.hashCode(); + } + + @Override + public int compareTo(@NotNull ClspMethod other) { + return this.methodInfo.compareTo(other.methodInfo); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ClspMth{"); + if (Utils.notEmpty(getTypeParameters())) { + sb.append('<'); + sb.append(Utils.listToString(getTypeParameters())); + sb.append("> "); + } + sb.append(getMethodInfo().getFullName()); + sb.append('('); + sb.append(Utils.listToString(getArgTypes())); + sb.append("):"); + sb.append(getReturnType()); + if (isVarArg()) { + sb.append(" VARARG"); + } + List throwsList = getThrows(); + if (Utils.notEmpty(throwsList)) { + sb.append(" throws ").append(Utils.listToString(throwsList)); + } + sb.append('}'); + return sb.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java index adf4addb2..95ae11ac2 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import org.slf4j.Logger; @@ -45,13 +46,15 @@ public class ConvertToClsSet { LOG.info("Loaded: {}", inputFile.getFile()); } - RootNode root = new RootNode(new JadxArgs()); + JadxArgs jadxArgs = new JadxArgs(); + jadxArgs.setRenameFlags(EnumSet.noneOf(JadxArgs.RenameEnum.class)); + RootNode root = new RootNode(jadxArgs); root.load(inputFiles); - ClsSet set = new ClsSet(); + ClsSet set = new ClsSet(root); set.loadFrom(root); set.save(output); - LOG.info("Output: {}", output); + LOG.info("Output: {}, file size: {}B", output, output.toFile().length()); LOG.info("done"); } diff --git a/jadx-core/src/main/java/jadx/core/clsp/NClass.java b/jadx-core/src/main/java/jadx/core/clsp/NClass.java deleted file mode 100644 index 08dfac6e8..000000000 --- a/jadx-core/src/main/java/jadx/core/clsp/NClass.java +++ /dev/null @@ -1,96 +0,0 @@ -package jadx.core.clsp; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import jadx.core.dex.nodes.GenericInfo; - -/** - * Class node in classpath graph - */ -public class NClass { - - private final String name; - private final int id; - private NClass[] parents; - private Map methodsMap = Collections.emptyMap(); - private List generics = Collections.emptyList(); - - public NClass(String name, int id) { - this.name = name; - this.id = id; - } - - public String getName() { - return name; - } - - public int getId() { - return id; - } - - public NClass[] getParents() { - return parents; - } - - public void setParents(NClass[] parents) { - this.parents = parents; - } - - public Map getMethodsMap() { - return methodsMap; - } - - public List getMethodsList() { - List list = new ArrayList<>(methodsMap.size()); - list.addAll(methodsMap.values()); - Collections.sort(list); - return list; - } - - public void setMethodsMap(Map methodsMap) { - this.methodsMap = Objects.requireNonNull(methodsMap); - } - - public void setMethods(List methods) { - Map map = new HashMap<>(methods.size()); - for (NMethod mth : methods) { - map.put(mth.getShortId(), mth); - } - setMethodsMap(map); - } - - public List getGenerics() { - return generics; - } - - public void setGenerics(List generics) { - this.generics = generics; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - NClass nClass = (NClass) o; - return name.equals(nClass.name); - } - - @Override - public String toString() { - return name; - } -} diff --git a/jadx-core/src/main/java/jadx/core/clsp/NMethod.java b/jadx-core/src/main/java/jadx/core/clsp/NMethod.java deleted file mode 100644 index 70cf7c084..000000000 --- a/jadx-core/src/main/java/jadx/core/clsp/NMethod.java +++ /dev/null @@ -1,92 +0,0 @@ -package jadx.core.clsp; - -import java.util.Arrays; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import jadx.core.dex.instructions.args.ArgType; - -/** - * Generic method node in classpath graph. - */ -public class NMethod implements Comparable { - - private final String shortId; - - /** - * Array contains only generic args, others set to 'null', size can be less than total args count - */ - @Nullable - private final ArgType[] genericArgs; - - @Nullable - private final ArgType retType; - - private final boolean varArgs; - - public NMethod(String shortId, @Nullable ArgType[] genericArgs, @Nullable ArgType retType, boolean varArgs) { - this.shortId = shortId; - this.genericArgs = genericArgs; - this.retType = retType; - this.varArgs = varArgs; - } - - public String getShortId() { - return shortId; - } - - @Nullable - public ArgType[] getGenericArgs() { - return genericArgs; - } - - @Nullable - public ArgType getGenericArg(int i) { - ArgType[] args = this.genericArgs; - if (args != null && i < args.length) { - return args[i]; - } - return null; - } - - @Nullable - public ArgType getReturnType() { - return retType; - } - - public boolean isVarArgs() { - return varArgs; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof NMethod)) { - return false; - } - NMethod other = (NMethod) o; - return shortId.equals(other.shortId); - } - - @Override - public int hashCode() { - return shortId.hashCode(); - } - - @Override - public int compareTo(@NotNull NMethod other) { - return this.shortId.compareTo(other.shortId); - } - - @Override - public String toString() { - return "NMethod{'" + shortId + '\'' - + ", argTypes=" + Arrays.toString(genericArgs) - + ", retType=" + retType - + ", varArgs=" + varArgs - + '}'; - } -} diff --git a/jadx-core/src/main/java/jadx/core/clsp/SimpleMethodDetails.java b/jadx-core/src/main/java/jadx/core/clsp/SimpleMethodDetails.java new file mode 100644 index 000000000..d1ada832c --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/SimpleMethodDetails.java @@ -0,0 +1,53 @@ +package jadx.core.clsp; + +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 SimpleMethodDetails implements IMethodDetails { + + private final MethodInfo methodInfo; + + public SimpleMethodDetails(MethodInfo methodInfo) { + this.methodInfo = methodInfo; + } + + @Override + public MethodInfo getMethodInfo() { + return methodInfo; + } + + @Override + public ArgType getReturnType() { + return methodInfo.getReturnType(); + } + + @Override + public List getArgTypes() { + return methodInfo.getArgumentsTypes(); + } + + @Override + public List getTypeParameters() { + return Collections.emptyList(); + } + + @Override + public List getThrows() { + return Collections.emptyList(); + } + + @Override + public boolean isVarArg() { + return false; + } + + @Override + public String toString() { + return "SimpleMethodDetails{" + methodInfo + '}'; + } +} diff --git a/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java b/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java index 7bcf11ab5..01f4f7889 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java @@ -113,13 +113,11 @@ public class AnnotationGen { return paramName; } - @SuppressWarnings("unchecked") public void addThrows(MethodNode mth, CodeWriter code) { - Annotation an = mth.getAnnotation(Consts.DALVIK_THROWS); - if (an != null) { - Object exs = an.getDefaultValue(); + List throwList = mth.getThrows(); + if (!throwList.isEmpty()) { code.add(" throws "); - for (Iterator it = ((List) exs).iterator(); it.hasNext();) { + for (Iterator it = throwList.iterator(); it.hasNext();) { ArgType ex = it.next(); classGen.useType(code, ex); if (it.hasNext()) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 4a83d5c35..5331a8e65 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -29,7 +29,7 @@ import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.GenericInfo; +import jadx.core.dex.nodes.GenericTypeParameter; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.parser.FieldInitAttr; @@ -149,7 +149,7 @@ public class ClassGen { clsCode.attachDefinition(cls); clsCode.add(cls.getClassInfo().getAliasShortName()); - addGenericMap(clsCode, cls.getGenerics(), true); + addGenericTypeParameters(clsCode, cls.getGenericTypeParameters(), true); clsCode.add(' '); ArgType sup = cls.getSuperClass(); @@ -180,17 +180,17 @@ public class ClassGen { } } - public boolean addGenericMap(CodeWriter code, List generics, boolean classDeclaration) { + public boolean addGenericTypeParameters(CodeWriter code, List generics, boolean classDeclaration) { if (generics == null || generics.isEmpty()) { return false; } code.add('<'); int i = 0; - for (GenericInfo genericInfo : generics) { + for (GenericTypeParameter genericInfo : generics) { if (i != 0) { code.add(", "); } - ArgType type = genericInfo.getGenericType(); + ArgType type = genericInfo.getTypeVariable(); if (type.isGenericType()) { code.add(type.getObject()); } else { diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 8de03f0b3..f6890d505 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -23,7 +23,7 @@ import jadx.core.dex.info.FieldInfo; 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.BaseInvokeNode; import jadx.core.dex.instructions.ConstClassNode; import jadx.core.dex.instructions.ConstStringNode; import jadx.core.dex.instructions.FillArrayNode; @@ -53,7 +53,6 @@ import jadx.core.dex.nodes.InsnNode; 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; @@ -621,7 +620,7 @@ public class InsnGen { code.add("new "); useClass(code, insn.getClassType()); ArgType argType = insn.getResult().getSVar().getCodeVar().getType(); - boolean genericCls = cls == null || !cls.getGenerics().isEmpty(); + boolean genericCls = cls == null || !cls.getGenericTypeParameters().isEmpty(); if (argType != null && argType.getGenericTypes() != null && genericCls) { @@ -761,30 +760,31 @@ public class InsnGen { return useCls.getParentClass().getClassInfo(); } - void generateMethodArguments(CodeWriter code, InsnNode insn, int startArgNum, - @Nullable MethodNode callMth) throws CodegenException { + void generateMethodArguments(CodeWriter code, BaseInvokeNode insn, int startArgNum, + @Nullable MethodNode mthNode) throws CodegenException { int k = startArgNum; - if (callMth != null && callMth.contains(AFlag.SKIP_FIRST_ARG)) { + if (mthNode != null && mthNode.contains(AFlag.SKIP_FIRST_ARG)) { k++; } int argsCount = insn.getArgsCount(); code.add('('); boolean firstArg = true; if (k < argsCount) { - boolean overloaded = callMth != null && callMth.isArgsOverload(); for (int i = k; i < argsCount; i++) { InsnArg arg = insn.getArg(i); if (arg.contains(AFlag.SKIP_ARG)) { continue; } - if (SkipMethodArgsAttr.isSkip(callMth, i - startArgNum)) { + int argOrigPos = i - startArgNum; + if (SkipMethodArgsAttr.isSkip(mthNode, argOrigPos)) { continue; } if (!firstArg) { code.add(", "); + } else { + firstArg = false; } - boolean cast = addArgCast(code, insn, callMth, arg, i - startArgNum, overloaded); - if (!cast && i == argsCount - 1 && processVarArg(code, callMth, arg)) { + if (i == argsCount - 1 && processVarArg(code, insn, arg)) { continue; } addArg(code, arg, false); @@ -794,91 +794,29 @@ public class InsnGen { code.add(')'); } - /** - * Add additional cast for method argument. - */ - private boolean addArgCast(CodeWriter code, InsnNode insn, @Nullable MethodNode callMth, - InsnArg arg, int origPos, boolean overloaded) { - ArgType castType = null; - if (callMth != null) { - List argTypes = callMth.getArgTypes(); - ArgType origType = argTypes.get(origPos); - if (origType.isGenericType() && !callMth.getParentClass().equals(mth.getParentClass())) { - // cancel cast - return false; - } - 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; - } - } else { - castType = arg.getType(); - } - // TODO: check castType for left type variables - - if (isCastNeeded(arg, castType, overloaded)) { - code.add('('); - useType(code, castType); - code.add(") "); - return true; - } - return false; - } - - private boolean isCastNeeded(InsnArg arg, ArgType origType, boolean overloaded) { - ArgType argType = arg.getType(); - if (arg.isLiteral() && ((LiteralArg) arg).getLiteral() == 0 - && (argType.isObject() || argType.isArray())) { - return true; - } - if (argType.equals(origType)) { - return false; - } - return overloaded; - } - /** * Expand varArgs from filled array. */ - private boolean processVarArg(CodeWriter code, MethodNode callMth, InsnArg lastArg) throws CodegenException { - if (callMth == null || !callMth.getAccessFlags().isVarArgs()) { + private boolean processVarArg(CodeWriter code, BaseInvokeNode invokeInsn, InsnArg lastArg) throws CodegenException { + if (!invokeInsn.contains(AFlag.VARARG_CALL)) { return false; } if (!lastArg.getType().isArray() || !lastArg.isInsnWrap()) { return false; } InsnNode insn = ((InsnWrapArg) lastArg).getWrapInsn(); - if (insn.getType() == InsnType.FILLED_NEW_ARRAY) { - int count = insn.getArgsCount(); - for (int i = 0; i < count; i++) { - InsnArg elemArg = insn.getArg(i); - addArg(code, elemArg, false); - if (i < count - 1) { - code.add(", "); - } - } - return true; + if (insn.getType() != InsnType.FILLED_NEW_ARRAY) { + return false; } - return false; + int count = insn.getArgsCount(); + for (int i = 0; i < count; i++) { + InsnArg elemArg = insn.getArg(i); + addArg(code, elemArg, false); + if (i < count - 1) { + code.add(", "); + } + } + return true; } private boolean inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException { @@ -936,7 +874,7 @@ public class InsnGen { if (parentInsn.contains(AFlag.WRAPPED)) { return false; } - return !callMthNode.getReturnType().equals(ArgType.VOID); + return !callMthNode.isVoidReturn(); } private void makeTernary(TernaryInsn insn, CodeWriter code, Set state) throws CodegenException { diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index b9ee51edb..09d4ed00f 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -98,8 +98,12 @@ public class MethodGen { if (Consts.DEBUG) { code.add(mth.isVirtual() ? "/* virtual */ " : "/* direct */ "); } + if (clsAccFlags.isInterface() && !mth.isNoCode()) { + // add 'default' for method with code in interface + code.add("default "); + } - if (classGen.addGenericMap(code, mth.getGenerics(), false)) { + if (classGen.addGenericTypeParameters(code, mth.getTypeParameters(), false)) { code.add(' '); } if (ai.isConstructor()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java index 69348444e..c4f862442 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java @@ -60,6 +60,7 @@ public enum AFlag { FALL_THROUGH, EXPLICIT_GENERICS, + VARARG_CALL, /** * Use constants with explicit type: cast '(byte) 1' or type letter '7L' diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java index 2cdab5b73..00d018af4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java @@ -24,6 +24,7 @@ import jadx.core.dex.attributes.nodes.RegDebugInfoAttr; import jadx.core.dex.attributes.nodes.RenameReasonAttr; import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr; +import jadx.core.dex.nodes.IMethodDetails; import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.ExcHandlerAttr; @@ -79,6 +80,7 @@ public class AType { // instruction public static final AType LOOP_LABEL = new AType<>(); public static final AType> JUMP = new AType<>(); + public static final AType METHOD_DETAILS = new AType<>(); // register public static final AType REG_DEBUG_INFO = new AType<>(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java index 789bac206..2d342b9d1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeStorage.java @@ -11,6 +11,7 @@ import java.util.Set; import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.attributes.annotations.AnnotationsList; import jadx.core.utils.Utils; +import jadx.core.utils.exceptions.JadxRuntimeException; /** * Storage for different attribute types: @@ -19,6 +20,13 @@ import jadx.core.utils.Utils; */ public class AttributeStorage { + static { + int flagsCount = AFlag.values().length; + if (flagsCount >= 64) { + throw new JadxRuntimeException("Try to reduce flags count to 64 for use one long in EnumSet, now " + flagsCount); + } + } + private final Set flags; private Map, IAttribute> attributes; @@ -127,7 +135,7 @@ public class AttributeStorage { list.add(a.toString()); } for (IAttribute a : attributes.values()) { - list.add(a.toString()); + list.add(a.toAttrString()); } return list; } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/IAttribute.java b/jadx-core/src/main/java/jadx/core/dex/attributes/IAttribute.java index cac361c26..9eb94d8e5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/IAttribute.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/IAttribute.java @@ -2,4 +2,8 @@ package jadx.core.dex.attributes; public interface IAttribute { AType getType(); + + default String toAttrString() { + return this.toString(); + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 7e166566f..e58e3b33c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -39,7 +39,7 @@ public final class ClassInfo implements Comparable { public static ClassInfo fromDex(DexNode dex, int clsIndex) { if (clsIndex == DexNode.NO_INDEX) { - return null; + throw new JadxRuntimeException("NO_INDEX for class"); } return fromType(dex.root(), dex.getType(clsIndex)); } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/InfoStorage.java b/jadx-core/src/main/java/jadx/core/dex/info/InfoStorage.java index 80e4e8f36..4996d18f8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/InfoStorage.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/InfoStorage.java @@ -5,12 +5,15 @@ import java.util.Map; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; +import jadx.core.utils.exceptions.JadxRuntimeException; public class InfoStorage { private final Map classes = new HashMap<>(); - private final Map methods = new HashMap<>(); private final Map fields = new HashMap<>(); + private final Map methods = new HashMap<>(); + // use only one MethodInfo instance + private final Map uniqueMethods = new HashMap<>(); public ClassInfo getCls(ArgType type) { return classes.get(type); @@ -31,10 +34,25 @@ public class InfoStorage { return methods.get(generateMethodLookupId(dex, mtdId)); } - public MethodInfo putMethod(DexNode dex, int mthId, MethodInfo mth) { + public MethodInfo putMethod(DexNode dex, int mthId, MethodInfo methodInfo) { synchronized (methods) { - MethodInfo prev = methods.put(generateMethodLookupId(dex, mthId), mth); - return prev == null ? mth : prev; + MethodInfo uniqueMethodInfo = putMethod(methodInfo); + MethodInfo prev = methods.put(generateMethodLookupId(dex, mthId), uniqueMethodInfo); + if (prev != null) { + throw new JadxRuntimeException("Method info already added: " + methodInfo); + } + return uniqueMethodInfo; + } + } + + public MethodInfo putMethod(MethodInfo newMth) { + synchronized (uniqueMethods) { + MethodInfo prev = uniqueMethods.get(newMth); + if (prev != null) { + return prev; + } + uniqueMethods.put(newMth, newMth); + return newMth; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java index a37c45dbc..f8058cb0f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java @@ -1,6 +1,9 @@ package jadx.core.dex.info; import java.util.List; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; import com.android.dex.MethodId; import com.android.dex.ProtoId; @@ -8,52 +11,48 @@ import com.android.dex.ProtoId; import jadx.core.codegen.TypeGen; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; +import jadx.core.dex.nodes.RootNode; import jadx.core.utils.Utils; -public final class MethodInfo { +public final class MethodInfo implements Comparable { private final String name; private final ArgType retType; - private final List args; + private final List argTypes; private final ClassInfo declClass; private final String shortId; private String alias; private boolean aliasFromPreset; - private MethodInfo(DexNode dex, int mthIndex) { - MethodId mthId = dex.getMethodId(mthIndex); - name = dex.getString(mthId.getNameIndex()); - alias = name; - aliasFromPreset = false; - declClass = ClassInfo.fromDex(dex, mthId.getDeclaringClassIndex()); - - ProtoId proto = dex.getProtoId(mthId.getProtoIndex()); - retType = dex.getType(proto.getReturnTypeIndex()); - args = dex.readParamList(proto.getParametersOffset()); - shortId = makeSignature(true); - } - private MethodInfo(ClassInfo declClass, String name, List args, ArgType retType) { this.name = name; this.alias = name; this.aliasFromPreset = false; this.declClass = declClass; - this.args = args; + this.argTypes = args; this.retType = retType; - this.shortId = makeSignature(true); - } - - public static MethodInfo externalMth(ClassInfo declClass, String name, List args, ArgType retType) { - return new MethodInfo(declClass, name, args, retType); + this.shortId = makeShortId(name, argTypes, retType); } public static MethodInfo fromDex(DexNode dex, int mthIndex) { - MethodInfo mth = dex.root().getInfoStorage().getMethod(dex, mthIndex); - if (mth != null) { - return mth; + MethodInfo storageMth = dex.root().getInfoStorage().getMethod(dex, mthIndex); + if (storageMth != null) { + return storageMth; } - mth = new MethodInfo(dex, mthIndex); - return dex.root().getInfoStorage().putMethod(dex, mthIndex, mth); + MethodId mthId = dex.getMethodId(mthIndex); + String mthName = dex.getString(mthId.getNameIndex()); + ClassInfo parentClass = ClassInfo.fromDex(dex, mthId.getDeclaringClassIndex()); + + ProtoId proto = dex.getProtoId(mthId.getProtoIndex()); + ArgType returnType = dex.getType(proto.getReturnTypeIndex()); + List args = dex.readParamList(proto.getParametersOffset()); + MethodInfo newMth = new MethodInfo(parentClass, mthName, args, returnType); + return dex.root().getInfoStorage().putMethod(dex, mthIndex, newMth); + } + + public static MethodInfo fromDetails(RootNode rootNode, ClassInfo declClass, String name, List args, ArgType retType) { + MethodInfo newMth = new MethodInfo(declClass, name, args, retType); + return rootNode.getInfoStorage().putMethod(newMth); } public String makeSignature(boolean includeRetType) { @@ -61,17 +60,29 @@ public final class MethodInfo { } public String makeSignature(boolean useAlias, boolean includeRetType) { - StringBuilder signature = new StringBuilder(); - signature.append(useAlias ? alias : name); - signature.append('('); - for (ArgType arg : args) { - signature.append(TypeGen.signature(arg)); + return makeShortId(useAlias ? alias : name, + argTypes, + includeRetType ? retType : null); + } + + public static String makeShortId(String name, List argTypes, @Nullable ArgType retType) { + StringBuilder sb = new StringBuilder(); + sb.append(name); + sb.append('('); + for (ArgType arg : argTypes) { + sb.append(TypeGen.signature(arg)); } - signature.append(')'); - if (includeRetType) { - signature.append(TypeGen.signature(retType)); + sb.append(')'); + if (retType != null) { + sb.append(TypeGen.signature(retType)); } - return signature.toString(); + return sb.toString(); + } + + public boolean isOverloadedBy(MethodInfo otherMthInfo) { + return argTypes.size() == otherMthInfo.argTypes.size() + && name.equals(otherMthInfo.name) + && !Objects.equals(this.shortId, otherMthInfo.shortId); } public String getName() { @@ -106,11 +117,11 @@ public final class MethodInfo { } public List getArgumentsTypes() { - return args; + return argTypes; } public int getArgsCount() { - return args.size(); + return argTypes.size(); } public boolean isConstructor() { @@ -143,10 +154,7 @@ public final class MethodInfo { @Override public int hashCode() { - int result = declClass.hashCode(); - result = 31 * result + retType.hashCode(); - result = 31 * result + shortId.hashCode(); - return result; + return shortId.hashCode() + 31 * declClass.hashCode(); } @Override @@ -159,13 +167,21 @@ public final class MethodInfo { } MethodInfo other = (MethodInfo) obj; return shortId.equals(other.shortId) - && retType.equals(other.retType) && declClass.equals(other.declClass); } + @Override + public int compareTo(MethodInfo other) { + int clsCmp = declClass.compareTo(other.declClass); + if (clsCmp != 0) { + return clsCmp; + } + return shortId.compareTo(other.shortId); + } + @Override public String toString() { return declClass.getFullName() + '.' + name - + '(' + Utils.listToString(args) + "):" + retType; + + '(' + Utils.listToString(argTypes) + "):" + retType; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/BaseInvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/BaseInvokeNode.java new file mode 100644 index 000000000..02234e741 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/BaseInvokeNode.java @@ -0,0 +1,25 @@ +package jadx.core.dex.instructions; + +import org.jetbrains.annotations.Nullable; + +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.nodes.InsnNode; + +public abstract class BaseInvokeNode extends InsnNode { + public BaseInvokeNode(InsnType type, int argsCount) { + super(type, argsCount); + } + + public abstract MethodInfo getCallMth(); + + @Nullable + public abstract InsnArg getInstanceArg(); + + public abstract boolean isStaticCall(); + + /** + * Return offset to match method args from {@link #getCallMth()} + */ + public abstract int getFirstArgOffset(); +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java b/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java deleted file mode 100644 index 823ca0e22..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java +++ /dev/null @@ -1,19 +0,0 @@ -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(); - - /** - * Return offset to match method args from {@link #getCallMth()} - */ - int getFirstArgOffset(); -} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java index f0099e76f..9b850c919 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java @@ -7,11 +7,10 @@ import com.android.dx.io.instructions.DecodedInstruction; import jadx.core.dex.info.MethodInfo; 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.nodes.InsnNode; import jadx.core.utils.InsnUtils; -public class InvokeNode extends InsnNode implements CallMthInterface { +public final class InvokeNode extends BaseInvokeNode { private final InvokeType type; private final MethodInfo mth; @@ -55,17 +54,18 @@ public class InvokeNode extends InsnNode implements CallMthInterface { @Override @Nullable - public RegisterArg getInstanceArg() { + public InsnArg getInstanceArg() { if (type != InvokeType.STATIC && getArgsCount() > 0) { - InsnArg firstArg = getArg(0); - if (firstArg.isRegister()) { - return ((RegisterArg) firstArg); - } + return getArg(0); } return null; } @Override + public boolean isStaticCall() { + return type == InvokeType.STATIC; + } + public int getFirstArgOffset() { return type == InvokeType.STATIC ? 0 : 1; } @@ -89,6 +89,6 @@ public class InvokeNode extends InsnNode implements CallMthInterface { @Override public String toString() { - return super.toString() + ' ' + mth + " type: " + type; + return super.toString() + " type: " + type + " call: " + mth; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 783e147d5..fe6fff83d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -10,7 +10,6 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.nodes.parser.SignatureParser; import jadx.core.utils.Utils; public abstract class ArgType { @@ -24,12 +23,13 @@ public abstract class ArgType { public static final ArgType LONG = primitive(PrimitiveType.LONG); public static final ArgType VOID = primitive(PrimitiveType.VOID); - public static final ArgType OBJECT = object(Consts.CLASS_OBJECT); - public static final ArgType CLASS = object(Consts.CLASS_CLASS); - public static final ArgType STRING = object(Consts.CLASS_STRING); - public static final ArgType ENUM = object(Consts.CLASS_ENUM); - public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE); + public static final ArgType OBJECT = objectNoCache(Consts.CLASS_OBJECT); + public static final ArgType CLASS = objectNoCache(Consts.CLASS_CLASS); + public static final ArgType STRING = objectNoCache(Consts.CLASS_STRING); + public static final ArgType ENUM = objectNoCache(Consts.CLASS_ENUM); + public static final ArgType THROWABLE = objectNoCache(Consts.CLASS_THROWABLE); public static final ArgType OBJECT_ARRAY = array(OBJECT); + public static final ArgType WILDCARD = wildcard(); public static final ArgType UNKNOWN = unknown(PrimitiveType.values()); public static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY); @@ -66,10 +66,25 @@ public abstract class ArgType { return new PrimitiveArg(stype); } - public static ArgType object(String obj) { + private static ArgType objectNoCache(String obj) { return new ObjectType(obj); } + public static ArgType object(String obj) { + // TODO: add caching + String cleanObjectName = Utils.cleanObjectName(obj); + switch (cleanObjectName) { + case Consts.CLASS_OBJECT: + return OBJECT; + case Consts.CLASS_STRING: + return STRING; + case Consts.CLASS_CLASS: + return CLASS; + default: + return new ObjectType(cleanObjectName); + } + } + public static ArgType genericType(String type) { return new GenericType(type); } @@ -82,12 +97,16 @@ public abstract class ArgType { return new WildcardType(obj, bound); } - public static ArgType parseGenericSignature(String sign) { - return new SignatureParser(sign).consumeType(); + public static ArgType generic(ArgType obj, ArgType... generics) { + if (!obj.isObject()) { + throw new IllegalArgumentException("Expected Object as ArgType, got: " + obj); + } + return new GenericObject(obj.getObject(), generics); } public static ArgType generic(String obj, ArgType... generics) { - return new GenericObject(obj, generics); + String cleanObjectName = Utils.cleanObjectName(obj); + return new GenericObject(cleanObjectName, generics); } public static ArgType outerGeneric(ArgType genericOuterType, ArgType innerType) { @@ -160,7 +179,7 @@ public abstract class ArgType { protected final String objName; public ObjectType(String obj) { - this.objName = Utils.cleanObjectName(obj); + this.objName = obj; this.hash = objName.hashCode(); } @@ -555,12 +574,12 @@ public abstract class ArgType { public abstract PrimitiveType[] getPossibleTypes(); - public static boolean isCastNeeded(DexNode dex, ArgType from, ArgType to) { + public static boolean isCastNeeded(RootNode root, ArgType from, ArgType to) { if (from.equals(to)) { return false; } if (from.isObject() && to.isObject() - && dex.root().getClsp().isImplements(from.getObject(), to.getObject())) { + && root.getClsp().isImplements(from.getObject(), to.getObject())) { return false; } return true; @@ -679,25 +698,44 @@ public abstract class ArgType { return 1; } - public boolean containsGenericType() { + public boolean containsGeneric() { + if (isGeneric() || isGenericType()) { + return true; + } + if (isArray()) { + ArgType arrayElement = getArrayElement(); + if (arrayElement != null) { + return arrayElement.containsGeneric(); + } + } + return false; + } + + public boolean containsTypeVariable() { if (isGenericType()) { return true; } ArgType wildcardType = getWildcardType(); if (wildcardType != null) { - return wildcardType.containsGenericType(); + return wildcardType.containsTypeVariable(); } if (isGeneric()) { ArgType[] genericTypes = getGenericTypes(); if (genericTypes != null) { for (ArgType genericType : genericTypes) { - if (genericType.containsGenericType()) { + if (genericType.containsTypeVariable()) { return true; } } } return false; } + if (isArray()) { + ArgType arrayElement = getArrayElement(); + if (arrayElement != null) { + return arrayElement.containsTypeVariable(); + } + } return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index 7a590cbc1..7a900d340 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -4,14 +4,14 @@ import org.jetbrains.annotations.Nullable; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.instructions.CallMthInterface; +import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -public final class ConstructorInsn extends InsnNode implements CallMthInterface { +public final class ConstructorInsn extends BaseInvokeNode { private final MethodInfo callMth; private final CallType callType; @@ -59,6 +59,7 @@ public final class ConstructorInsn extends InsnNode implements CallMthInterface this.callType = callType; } + @Override public MethodInfo getCallMth() { return callMth; } @@ -93,6 +94,11 @@ public final class ConstructorInsn extends InsnNode implements CallMthInterface return callType == CallType.SELF; } + @Override + public boolean isStaticCall() { + return false; + } + @Override public int getFirstArgOffset() { return 0; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 9ba4da5a6..8d3867083 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -54,7 +54,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { private AccessInfo accessFlags; private ArgType superClass; private List interfaces; - private List generics = Collections.emptyList(); + private List generics = Collections.emptyList(); private final List methods; private final List fields; @@ -80,6 +80,10 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { try { if (cls.getSupertypeIndex() == DexNode.NO_INDEX) { this.superClass = null; + // only java.lang.Object don't have super class + if (!clsInfo.getType().getObject().equals(Consts.CLASS_OBJECT)) { + throw new JadxRuntimeException("No super class in " + clsInfo.getType()); + } } else { this.superClass = dex.getType(cls.getSupertypeIndex()); } @@ -197,7 +201,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { } try { // parse class generic map - generics = sp.consumeGenericMap(); + generics = sp.consumeGenericTypeParameters(); // parse super class signature superClass = sp.consumeType(); // parse interfaces signatures @@ -335,7 +339,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { return interfaces; } - public List getGenerics() { + public List getGenericTypeParameters() { return generics; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/GenericInfo.java b/jadx-core/src/main/java/jadx/core/dex/nodes/GenericInfo.java deleted file mode 100644 index e70e5649b..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/GenericInfo.java +++ /dev/null @@ -1,46 +0,0 @@ -package jadx.core.dex.nodes; - -import java.util.List; - -import jadx.core.dex.instructions.args.ArgType; - -public class GenericInfo { - private final ArgType genericType; - private final List extendsList; - - public GenericInfo(ArgType genericType, List extendsList) { - this.genericType = genericType; - this.extendsList = extendsList; - } - - public ArgType getGenericType() { - return genericType; - } - - public List getExtendsList() { - return extendsList; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - GenericInfo other = (GenericInfo) o; - return genericType.equals(other.genericType) - && extendsList.equals(other.extendsList); - } - - @Override - public int hashCode() { - return 31 * genericType.hashCode() + extendsList.hashCode(); - } - - @Override - public String toString() { - return "GenericInfo{" + genericType + " extends: " + extendsList + '}'; - } -} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/GenericTypeParameter.java b/jadx-core/src/main/java/jadx/core/dex/nodes/GenericTypeParameter.java new file mode 100644 index 000000000..61f974755 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/GenericTypeParameter.java @@ -0,0 +1,55 @@ +package jadx.core.dex.nodes; + +import java.util.List; + +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.utils.Utils; + +import static jadx.core.utils.Utils.notEmpty; + +public class GenericTypeParameter { + private final ArgType typeVariable; + private final List extendsList; + + public GenericTypeParameter(ArgType typeVariable, List extendsList) { + this.typeVariable = typeVariable; + this.extendsList = extendsList; + } + + public ArgType getTypeVariable() { + return typeVariable; + } + + public List getExtendsList() { + return extendsList; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GenericTypeParameter other = (GenericTypeParameter) o; + return typeVariable.equals(other.typeVariable) + && extendsList.equals(other.extendsList); + } + + @Override + public int hashCode() { + return 31 * typeVariable.hashCode() + extendsList.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(typeVariable); + if (notEmpty(extendsList)) { + sb.append(" extends "); + sb.append(Utils.listToString(extendsList, " & ")); + } + return sb.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/IMethodDetails.java b/jadx-core/src/main/java/jadx/core/dex/nodes/IMethodDetails.java new file mode 100644 index 000000000..eaf04388f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/IMethodDetails.java @@ -0,0 +1,52 @@ +package jadx.core.dex.nodes; + +import java.util.List; + +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.IAttribute; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.utils.Utils; + +public interface IMethodDetails extends IAttribute { + + MethodInfo getMethodInfo(); + + ArgType getReturnType(); + + List getArgTypes(); + + List getTypeParameters(); + + List getThrows(); + + boolean isVarArg(); + + @Override + default AType getType() { + return AType.METHOD_DETAILS; + } + + @Override + default String toAttrString() { + StringBuilder sb = new StringBuilder(); + sb.append("MD:"); + if (Utils.notEmpty(getTypeParameters())) { + sb.append('<'); + sb.append(Utils.listToString(getTypeParameters())); + sb.append(">:"); + } + sb.append('('); + sb.append(Utils.listToString(getArgTypes())); + sb.append("):"); + sb.append(getReturnType()); + if (isVarArg()) { + sb.append(" VARARG"); + } + List throwsList = getThrows(); + if (Utils.notEmpty(throwsList)) { + sb.append(" throws ").append(Utils.listToString(throwsList)); + } + return sb.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index 703c824c5..86fee5ce1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -404,7 +404,7 @@ public class InsnNode extends LineAttrNode { protected void appendArgs(StringBuilder sb) { String argsStr = Utils.listToString(arguments); - if (argsStr.length() < 60) { + if (argsStr.length() < 120) { sb.append(argsStr); } else { // wrap args 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 bfda66775..fd2aee0fd 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 @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import org.jetbrains.annotations.NotNull; @@ -16,8 +17,10 @@ import com.android.dex.Code; import com.android.dex.Code.CatchHandler; import com.android.dex.Code.Try; +import jadx.core.Consts; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.attributes.nodes.JumpInfo; import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.attributes.nodes.LoopInfo; @@ -46,7 +49,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException; import static jadx.core.utils.Utils.lockList; -public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { +public class MethodNode extends LineAttrNode implements IMethodDetails, ILoadable, ICodeNode { private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class); private final MethodInfo mthInfo; @@ -66,7 +69,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { // additional info available after load, keep on unload private ArgType retType; private List argTypes; - private List generics; + private List typeParameters; // decompilation data, reset on unload private RegisterArg thisArg; @@ -96,7 +99,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { if (noCode) { return; } - // don't unload retType, argTypes, generics + // don't unload retType, argTypes, typeParameters thisArg = null; argsList = null; sVars = Collections.emptyList(); @@ -190,7 +193,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return null; } try { - this.generics = sp.consumeGenericMap(); + this.typeParameters = sp.consumeGenericTypeParameters(); List argsTypes = sp.consumeMethodArgs(); this.retType = sp.consumeType(); @@ -264,17 +267,32 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { } } + @Override @NotNull public List getArgTypes() { if (argTypes == null) { - throw new JadxRuntimeException("Method types not initialized: " + this); + throw new JadxRuntimeException("Method generic types not initialized: " + this); } return argTypes; } + public boolean containsGenericArgs() { + return !Objects.equals(mthInfo.getArgumentsTypes(), getArgTypes()); + } + + @Override + @NotNull + public ArgType getReturnType() { + return retType; + } + + public boolean isVoidReturn() { + return mthInfo.getReturnType().equals(ArgType.VOID); + } + public List getArgRegs() { if (argsList == null) { - throw new JadxRuntimeException("Method args not loaded: " + this + throw new JadxRuntimeException("Method arg registers not loaded: " + this + ", class status: " + parentClass.getTopParentClass().getState()); } return argsList; @@ -300,12 +318,9 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { this.add(AFlag.SKIP_FIRST_ARG); } - public ArgType getReturnType() { - return retType; - } - - public List getGenerics() { - return generics; + @Override + public List getTypeParameters() { + return typeParameters; } private static void initTryCatches(MethodNode mth, Code mthCode, InsnNode[] insnByOffset) { @@ -575,25 +590,31 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return exceptionHandlers.size(); } + @Override + @SuppressWarnings("unchecked") + public List getThrows() { + Annotation an = getAnnotation(Consts.DALVIK_THROWS); + if (an == null) { + return Collections.emptyList(); + } + return (List) an.getDefaultValue(); + } + /** * Return true if exists method with same name and arguments count */ - public boolean isArgsOverload() { - int argsCount = mthInfo.getArgumentsTypes().size(); - if (argsCount == 0) { - return false; - } - - String name = getName(); + public boolean isArgsOverloaded() { + MethodInfo thisMthInfo = this.mthInfo; + // quick check in current class for (MethodNode method : parentClass.getMethods()) { - MethodInfo otherMthInfo = method.mthInfo; - if (this != method - && otherMthInfo.getArgumentsTypes().size() == argsCount - && otherMthInfo.getName().equals(name)) { + if (method == this) { + continue; + } + if (method.getMethodInfo().isOverloadedBy(thisMthInfo)) { return true; } } - return false; + return root().getMethodUtils().isMethodArgsOverloaded(parentClass.getClassInfo().getType(), thisMthInfo); } public boolean isConstructor() { @@ -721,6 +742,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { ErrorsCounter.methodError(this, errStr, e); } + @Override public MethodInfo getMethodInfo() { return mthInfo; } @@ -743,6 +765,11 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return -1; } + @Override + public boolean isVarArg() { + return accFlags.isVarArgs(); + } + public boolean isLoaded() { return loaded; } @@ -767,7 +794,7 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { @Override public String toString() { return parentClass + "." + mthInfo.getName() - + '(' + Utils.listToString(mthInfo.getArgumentsTypes()) + "):" + + '(' + Utils.listToString(argTypes) + "):" + retType; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 781a047d5..32d20823d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -16,14 +16,14 @@ import jadx.api.ResourceType; 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; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.InfoStorage; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.nodes.utils.MethodUtils; +import jadx.core.dex.nodes.utils.TypeUtils; import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.typeinference.TypeUpdate; import jadx.core.utils.CacheStorage; @@ -48,6 +48,8 @@ public class RootNode { private final InfoStorage infoStorage = new InfoStorage(); private final CacheStorage cacheStorage = new CacheStorage(); private final TypeUpdate typeUpdate; + private final MethodUtils methodUtils; + private final TypeUtils typeUtils; private final ICodeCache codeCache; @@ -65,6 +67,10 @@ public class RootNode { this.constValues = new ConstStorage(args); this.typeUpdate = new TypeUpdate(this); this.codeCache = args.getCodeCache(); + this.methodUtils = new MethodUtils(this); + this.typeUtils = new TypeUtils(this); + + this.dexNodes = Collections.emptyList(); } public void load(List inputFiles) { @@ -119,7 +125,7 @@ public class RootNode { public void initClassPath() { try { if (this.clsp == null) { - ClspGraph newClsp = new ClspGraph(); + ClspGraph newClsp = new ClspGraph(this); newClsp.load(); List classes = new ArrayList<>(); @@ -168,6 +174,20 @@ public class RootNode { return null; } + @Nullable + public ClassNode resolveClass(ArgType clsType) { + if (clsType.isGeneric()) { + clsType = ArgType.object(clsType.getObject()); + } + for (DexNode dexNode : dexNodes) { + ClassNode cls = dexNode.resolveClass(clsType); + if (cls != null) { + return cls; + } + } + return null; + } + @Nullable public ClassNode searchClassByName(String fullName) { ClassInfo clsInfo = ClassInfo.fromName(this, fullName); @@ -206,6 +226,10 @@ public class RootNode { if (cls == null) { return null; } + MethodNode methodNode = cls.searchMethod(mth); + if (methodNode != null) { + return methodNode; + } return cls.dex().deepResolveMethod(cls, mth.makeSignature(false)); } @@ -232,60 +256,6 @@ public class RootNode { } } - @Nullable - public ArgType getMethodGenericReturnType(MethodInfo callMth) { - MethodNode methodNode = deepResolveMethod(callMth); - if (methodNode != null) { - ArgType returnType = methodNode.getReturnType(); - if (returnType != null && (returnType.isGeneric() || returnType.isGenericType())) { - return returnType; - } - return null; - } - NMethod methodDetails = clsp.getMethodDetails(callMth); - if (methodDetails != null) { - return methodDetails.getReturnType(); - } - return null; - } - - public List 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 argTypes = callMth.getArgumentsTypes(); - int argsCount = argTypes.size(); - List 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 getClassGenerics(ArgType type) { - ClassNode classNode = resolveClass(ClassInfo.fromType(this, type)); - if (classNode != null) { - return classNode.getGenerics(); - } - NClass clsDetails = getClsp().getClsDetails(type); - if (clsDetails == null || clsDetails.getGenerics().isEmpty()) { - return Collections.emptyList(); - } - List generics = clsDetails.getGenerics(); - return generics == null ? Collections.emptyList() : generics; - } - public List getDexNodes() { return dexNodes; } @@ -335,4 +305,11 @@ public class RootNode { return codeCache; } + public MethodUtils getMethodUtils() { + return methodUtils; + } + + public TypeUtils getTypeUtils() { + return typeUtils; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java index ebdb3fbaf..4b70dfd7c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,7 +13,7 @@ import jadx.core.Consts; import jadx.core.dex.attributes.IAttributeNode; import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.nodes.GenericInfo; +import jadx.core.dex.nodes.GenericTypeParameter; import jadx.core.utils.exceptions.JadxRuntimeException; public class SignatureParser { @@ -65,32 +66,40 @@ public class SignatureParser { * @return string from 'mark' to current position (not including current character) */ private String slice() { - if (mark >= pos) { + int start = mark == -1 ? 0 : mark; + if (start >= pos) { return ""; } - return sign.substring(mark, pos); + return sign.substring(start, pos); } /** * Inclusive slice (includes current character) */ private String inclusiveSlice() { - if (mark >= pos) { + int start = mark; + if (start == -1) { + start = 0; + } + int last = pos + 1; + if (start >= last) { return ""; } - return sign.substring(mark, pos + 1); + return sign.substring(start, last); } - private boolean forwardTo(char lastChar) { + private boolean skipUntil(char untilChar) { int startPos = pos; - char ch; - while ((ch = next()) != STOP_CHAR) { - if (ch == lastChar) { + while (true) { + if (lookAhead(untilChar)) { return true; } + char ch = next(); + if (ch == STOP_CHAR) { + pos = startPos; + return false; + } } - pos = startPos; - return false; } private void consume(char exp) { @@ -109,14 +118,14 @@ public class SignatureParser { return false; } - private String consumeUntil(char lastChar) { + @Nullable + public String consumeUntil(char lastChar) { mark(); - return forwardTo(lastChar) ? slice() : null; + return skipUntil(lastChar) ? inclusiveSlice() : null; } public ArgType consumeType() { char ch = next(); - mark(); switch (ch) { case 'L': ArgType obj = consumeObjectType(false); @@ -127,10 +136,13 @@ public class SignatureParser { case 'T': next(); mark(); - if (forwardTo(';')) { - return ArgType.genericType(slice()); + String typeVarName = consumeUntil(';'); + if (typeVarName != null) { + consume(';'); + return ArgType.genericType(typeVarName); } break; + case '[': return ArgType.array(consumeType()); @@ -145,7 +157,7 @@ public class SignatureParser { } break; } - throw new JadxRuntimeException("Can't parse type: " + debugString()); + throw new JadxRuntimeException("Can't parse type: " + debugString() + ", unexpected: " + ch); } private ArgType consumeObjectType(boolean incompleteType) { @@ -220,11 +232,11 @@ public class SignatureParser { *

* Example: "" */ - public List consumeGenericMap() { + public List consumeGenericTypeParameters() { if (!lookAhead('<')) { return Collections.emptyList(); } - List list = new ArrayList<>(); + List list = new ArrayList<>(); consume('<'); while (true) { if (lookAhead('>') || next() == STOP_CHAR) { @@ -232,12 +244,13 @@ public class SignatureParser { } String id = consumeUntil(':'); if (id == null) { - LOG.error("Failed to parse generic map: {}", sign); + LOG.error("Failed to parse generic types map: {}", sign); return Collections.emptyList(); } + consume(':'); tryConsume(':'); List types = consumeExtendsTypesList(); - list.add(new GenericInfo(ArgType.genericType(id), types)); + list.add(new GenericTypeParameter(ArgType.genericType(id), types)); } consume('>'); return list; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java new file mode 100644 index 000000000..67f24e708 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java @@ -0,0 +1,125 @@ +package jadx.core.dex.nodes.utils; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import jadx.core.clsp.ClspClass; +import jadx.core.clsp.ClspMethod; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.BaseInvokeNode; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.IMethodDetails; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; + +public class MethodUtils { + private final RootNode root; + + public MethodUtils(RootNode rootNode) { + this.root = rootNode; + } + + @Nullable + public IMethodDetails getMethodDetails(BaseInvokeNode invokeNode) { + IMethodDetails methodDetails = invokeNode.get(AType.METHOD_DETAILS); + if (methodDetails != null) { + return methodDetails; + } + return getMethodDetails(invokeNode.getCallMth()); + } + + @Nullable + public IMethodDetails getMethodDetails(MethodInfo callMth) { + MethodNode mthNode = root.deepResolveMethod(callMth); + if (mthNode != null) { + return mthNode; + } + return root.getClsp().getMethodDetails(callMth); + } + + /** + * Search methods with same name and args count in class hierarchy starting from {@code startCls} + * Beware {@code startCls} can be different from {@code mthInfo.getDeclClass()} + */ + public boolean isMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo) { + return processMethodArgsOverloaded(startCls, mthInfo, null); + } + + public List collectOverloadedMethods(ArgType startCls, MethodInfo mthInfo) { + List list = new ArrayList<>(); + processMethodArgsOverloaded(startCls, mthInfo, list); + return list; + } + + @Nullable + public ArgType getMethodGenericReturnType(BaseInvokeNode invokeNode) { + IMethodDetails methodDetails = getMethodDetails(invokeNode); + if (methodDetails != null) { + ArgType returnType = methodDetails.getReturnType(); + if (returnType != null && returnType.containsGeneric()) { + return returnType; + } + } + return null; + } + + public boolean processMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo, @Nullable List collectedMths) { + if (startCls == null) { + return false; + } + boolean isMthConstructor = mthInfo.isConstructor() || mthInfo.isClassInit(); + ClassNode classNode = root.resolveClass(startCls); + if (classNode != null) { + for (MethodNode mth : classNode.getMethods()) { + if (mthInfo.isOverloadedBy(mth.getMethodInfo())) { + if (collectedMths == null) { + return true; + } + collectedMths.add(mth); + } + } + if (!isMthConstructor) { + if (processMethodArgsOverloaded(classNode.getSuperClass(), mthInfo, collectedMths)) { + if (collectedMths == null) { + return true; + } + } + for (ArgType parentInterface : classNode.getInterfaces()) { + if (processMethodArgsOverloaded(parentInterface, mthInfo, collectedMths)) { + if (collectedMths == null) { + return true; + } + } + } + } + } else { + ClspClass clsDetails = root.getClsp().getClsDetails(startCls); + if (clsDetails == null) { + // class info not available + return false; + } + for (ClspMethod clspMth : clsDetails.getMethodsMap().values()) { + if (mthInfo.isOverloadedBy(clspMth.getMethodInfo())) { + if (collectedMths == null) { + return true; + } + collectedMths.add(clspMth); + } + } + if (!isMthConstructor) { + for (ArgType parent : clsDetails.getParents()) { + if (processMethodArgsOverloaded(parent, mthInfo, collectedMths)) { + if (collectedMths == null) { + return true; + } + } + } + } + } + return false; + } +} 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 new file mode 100644 index 000000000..0bb936df0 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/TypeUtils.java @@ -0,0 +1,142 @@ +package jadx.core.dex.nodes.utils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import jadx.core.clsp.ClspClass; +import jadx.core.dex.instructions.BaseInvokeNode; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.GenericTypeParameter; +import jadx.core.dex.nodes.IMethodDetails; +import jadx.core.dex.nodes.RootNode; + +public class TypeUtils { + private final RootNode root; + + public TypeUtils(RootNode rootNode) { + this.root = rootNode; + } + + @NotNull + public List getClassGenerics(ArgType type) { + ClassNode classNode = root.resolveClass(type); + if (classNode != null) { + return classNode.getGenericTypeParameters(); + } + ClspClass clsDetails = root.getClsp().getClsDetails(type); + if (clsDetails == null || clsDetails.getTypeParameters().isEmpty()) { + return Collections.emptyList(); + } + List generics = clsDetails.getTypeParameters(); + return generics == null ? Collections.emptyList() : generics; + } + + /** + * Replace generic types in {@code typeWithGeneric} using instance types + *
+ * Example: + *

    + *
  • {@code instanceType: Set} + *
  • {@code typeWithGeneric: Iterator} + *
  • {@code return: Iterator} + *
+ */ + @Nullable + public ArgType replaceClassGenerics(ArgType instanceType, ArgType typeWithGeneric) { + if (typeWithGeneric != null) { + Map replaceMap = getTypeVariablesMapping(instanceType); + if (!replaceMap.isEmpty()) { + return replaceTypeVariablesUsingMap(typeWithGeneric, replaceMap); + } + } + return null; + } + + public Map getTypeVariablesMapping(ArgType clsType) { + if (!clsType.isGeneric()) { + return Collections.emptyMap(); + } + + List typeParameters = root.getTypeUtils().getClassGenerics(clsType); + if (typeParameters.isEmpty()) { + return Collections.emptyMap(); + } + ArgType[] actualTypes = clsType.getGenericTypes(); + if (actualTypes == null) { + return Collections.emptyMap(); + } + int genericParamsCount = actualTypes.length; + if (genericParamsCount != typeParameters.size()) { + return Collections.emptyMap(); + } + Map replaceMap = new HashMap<>(genericParamsCount); + for (int i = 0; i < genericParamsCount; i++) { + ArgType actualType = actualTypes[i]; + ArgType genericType = typeParameters.get(i).getTypeVariable(); + replaceMap.put(genericType, actualType); + } + return replaceMap; + } + + @Nullable + public ArgType replaceMethodGenerics(BaseInvokeNode invokeInsn, IMethodDetails details, ArgType typeWithGeneric) { + if (typeWithGeneric == null) { + return null; + } + List methodArgTypes = details.getArgTypes(); + if (methodArgTypes.isEmpty()) { + return null; + } + int firstArgOffset = invokeInsn.getFirstArgOffset(); + int argsCount = methodArgTypes.size(); + for (int i = 0; i < argsCount; i++) { + ArgType methodArgType = methodArgTypes.get(i); + InsnArg insnArg = invokeInsn.getArg(i + firstArgOffset); + ArgType insnType = insnArg.getType(); + if (methodArgType.equals(typeWithGeneric)) { + return insnType; + } + } + // TODO build complete map for type variables + return null; + } + + @Nullable + public ArgType replaceTypeVariablesUsingMap(ArgType replaceType, Map replaceMap) { + if (replaceType.isGenericType()) { + return replaceMap.get(replaceType); + } + + ArgType wildcardType = replaceType.getWildcardType(); + if (wildcardType != null && wildcardType.containsTypeVariable()) { + ArgType newWildcardType = replaceTypeVariablesUsingMap(wildcardType, replaceMap); + if (newWildcardType == null) { + return null; + } + return ArgType.wildcard(newWildcardType, replaceType.getWildcardBound()); + } + + 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 = replaceTypeVariablesUsingMap(genericType, replaceMap); + if (type == null) { + type = genericType; + } + newTypes[i] = type; + } + return ArgType.generic(replaceType.getObject(), newTypes); + } + return null; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/AttachMethodDetails.java b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachMethodDetails.java new file mode 100644 index 000000000..24e2ec09a --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachMethodDetails.java @@ -0,0 +1,50 @@ +package jadx.core.dex.visitors; + +import jadx.core.dex.instructions.BaseInvokeNode; +import jadx.core.dex.nodes.BlockNode; +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.nodes.utils.MethodUtils; +import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; +import jadx.core.utils.exceptions.JadxException; + +@JadxVisitor( + name = "Attach Method Details", + desc = "Attach method details for invoke instructions", + runBefore = { + CodeShrinkVisitor.class + } +) +public class AttachMethodDetails extends AbstractVisitor { + + private MethodUtils methodUtils; + + @Override + public void init(RootNode root) { + methodUtils = root.getMethodUtils(); + } + + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + for (BlockNode blockNode : mth.getBasicBlocks()) { + for (InsnNode insn : blockNode.getInstructions()) { + if (insn instanceof BaseInvokeNode) { + attachMethodDetails((BaseInvokeNode) insn); + } + } + } + } + + private void attachMethodDetails(BaseInvokeNode insn) { + IMethodDetails methodDetails = methodUtils.getMethodDetails(insn.getCallMth()); + if (methodDetails != null) { + insn.addAttr(methodDetails); + } + } + +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java index 44c210c69..4dfe07854 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java @@ -5,7 +5,7 @@ import java.util.List; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.instructions.CallMthInterface; +import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.ConstStringNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; @@ -240,8 +240,8 @@ public class ConstInlineVisitor extends AbstractVisitor { } private static boolean needExplicitCast(InsnNode insn, LiteralArg arg) { - if (insn instanceof CallMthInterface) { - CallMthInterface callInsn = (CallMthInterface) insn; + if (insn instanceof BaseInvokeNode) { + BaseInvokeNode callInsn = (BaseInvokeNode) insn; MethodInfo callMth = callInsn.getCallMth(); int offset = callInsn.getFirstArgOffset(); int argIndex = insn.getArgIndex(arg); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java index 4dd248496..d10fbee2c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java @@ -54,7 +54,7 @@ public class DeboxingVisitor extends AbstractVisitor { private static MethodInfo valueOfMth(RootNode root, ArgType argType, String clsName) { ArgType boxType = ArgType.object(clsName); ClassInfo boxCls = ClassInfo.fromType(root, boxType); - return MethodInfo.externalMth(boxCls, "valueOf", Collections.singletonList(argType), boxType); + return MethodInfo.fromDetails(root, boxCls, "valueOf", Collections.singletonList(argType), boxType); } @Override 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 new file mode 100644 index 000000000..acd725e23 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInvokeVisitor.java @@ -0,0 +1,372 @@ +package jadx.core.dex.visitors; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import jadx.core.Consts; +import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.BaseInvokeNode; +import jadx.core.dex.instructions.IndexInsnNode; +import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.InsnWrapArg; +import jadx.core.dex.instructions.args.LiteralArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.mods.ConstructorInsn; +import jadx.core.dex.nodes.BlockNode; +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.shrink.CodeShrinkVisitor; +import jadx.core.dex.visitors.typeinference.TypeCompare; +import jadx.core.dex.visitors.typeinference.TypeCompareEnum; +import jadx.core.utils.Utils; +import jadx.core.utils.exceptions.JadxRuntimeException; + +@JadxVisitor( + name = "MethodInvokeVisitor", + desc = "Process additional info for method invocation (overload, vararg)", + runAfter = { + CodeShrinkVisitor.class, + ModVisitor.class + }, + runBefore = { + SimplifyVisitor.class // run before cast remove and StringBuilder replace + } +) +public class MethodInvokeVisitor extends AbstractVisitor { + private RootNode root; + + @Override + public void init(RootNode root) { + this.root = root; + } + + @Override + public void visit(MethodNode mth) { + if (mth.isNoCode()) { + return; + } + for (BlockNode block : mth.getBasicBlocks()) { + if (block.contains(AFlag.DONT_GENERATE)) { + continue; + } + for (InsnNode insn : block.getInstructions()) { + if (insn.contains(AFlag.DONT_GENERATE)) { + continue; + } + processInsn(mth, insn); + } + } + } + + private void processInsn(MethodNode mth, InsnNode insn) { + if (insn instanceof BaseInvokeNode) { + processInvoke(mth, ((BaseInvokeNode) insn)); + } + for (InsnArg insnArg : insn.getArguments()) { + if (insnArg instanceof InsnWrapArg) { + InsnNode wrapInsn = ((InsnWrapArg) insnArg).getWrapInsn(); + processInsn(mth, wrapInsn); + } + } + } + + private void processInvoke(MethodNode parentMth, BaseInvokeNode invokeInsn) { + MethodInfo callMth = invokeInsn.getCallMth(); + if (callMth.getArgsCount() == 0) { + return; + } + IMethodDetails mthDetails = root.getMethodUtils().getMethodDetails(invokeInsn); + if (mthDetails == null) { + if (Consts.DEBUG) { + parentMth.addComment("JADX DEBUG: Method info not found: " + callMth); + } + processUnknown(invokeInsn); + } else { + // parentMth.addComment("JADX DEBUG: got method details: " + mthDetails); + if (mthDetails.isVarArg()) { + ArgType last = Utils.last(mthDetails.getArgTypes()); + if (last != null && last.isArray()) { + invokeInsn.add(AFlag.VARARG_CALL); + } + } + processOverloaded(parentMth, invokeInsn, mthDetails); + } + } + + private void processOverloaded(MethodNode parentMth, BaseInvokeNode invokeInsn, IMethodDetails mthDetails) { + MethodInfo callMth = invokeInsn.getCallMth(); + ArgType callCls = getCallClassFromInvoke(parentMth, invokeInsn, callMth); + List overloadMethods = root.getMethodUtils().collectOverloadedMethods(callCls, callMth); + if (overloadMethods.isEmpty()) { + // not overloaded + return; + } + + overloadMethods.add(mthDetails); + resolveTypeVariablesInMethodArgs(invokeInsn, mthDetails, overloadMethods); + + int argsOffset = invokeInsn.getFirstArgOffset(); + List compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset); + List castTypes = searchCastTypes(parentMth, mthDetails, overloadMethods, compilerVarTypes); + applyArgsCast(invokeInsn, argsOffset, compilerVarTypes, castTypes); + } + + /** + * Method details not found => add cast for 'null' args + */ + private void processUnknown(BaseInvokeNode invokeInsn) { + int argsOffset = invokeInsn.getFirstArgOffset(); + List compilerVarTypes = collectCompilerVarTypes(invokeInsn, argsOffset); + List castTypes = new ArrayList<>(compilerVarTypes); + if (replaceUnknownTypes(castTypes, invokeInsn.getCallMth().getArgumentsTypes())) { + applyArgsCast(invokeInsn, argsOffset, compilerVarTypes, castTypes); + } + } + + private ArgType getCallClassFromInvoke(MethodNode parentMth, BaseInvokeNode invokeInsn, MethodInfo callMth) { + if (invokeInsn instanceof ConstructorInsn) { + ConstructorInsn constrInsn = (ConstructorInsn) invokeInsn; + if (constrInsn.isSuper()) { + return parentMth.getParentClass().getSuperClass(); + } + } + InsnArg instanceArg = invokeInsn.getInstanceArg(); + if (instanceArg != null) { + return instanceArg.getType(); + } + // static call + return callMth.getDeclClass().getType(); + } + + private void resolveTypeVariablesInMethodArgs(BaseInvokeNode invokeInsn, IMethodDetails mthDetails, + List overloadedMethods) { + MethodInfo callMth = invokeInsn.getCallMth(); + ArgType declClsType = callMth.getDeclClass().getType(); + ArgType callClsType; + InsnArg instanceArg = invokeInsn.getInstanceArg(); + if (instanceArg != null) { + callClsType = instanceArg.getType(); + } else { + callClsType = declClsType; + } + + Map typeVarsMapping = root.getTypeUtils().getTypeVariablesMapping(callClsType); + resolveTypeVars(mthDetails, typeVarsMapping); + for (IMethodDetails m : overloadedMethods) { + resolveTypeVars(m, typeVarsMapping); + } + } + + private void applyArgsCast(BaseInvokeNode invokeInsn, int argsOffset, List compilerVarTypes, List castTypes) { + int argsCount = invokeInsn.getArgsCount(); + for (int i = argsOffset; i < argsCount; i++) { + InsnArg arg = invokeInsn.getArg(i); + int origPos = i - argsOffset; + ArgType compilerType = compilerVarTypes.get(origPos); + ArgType castType = castTypes.get(origPos); + if (castType != null) { + if (!castType.equals(compilerType)) { + if (arg.isLiteral() && compilerType.isPrimitive() && castType.isPrimitive()) { + arg.setType(castType); + arg.add(AFlag.EXPLICIT_PRIMITIVE_TYPE); + } else { + InsnNode castInsn = new IndexInsnNode(InsnType.CAST, castType, 1); + castInsn.addArg(arg); + castInsn.add(AFlag.EXPLICIT_CAST); + InsnArg wrapCast = InsnArg.wrapArg(castInsn); + wrapCast.setType(castType); + invokeInsn.setArg(i, wrapCast); + } + } else { + // protect already existed cast + if (arg.isInsnWrap()) { + InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); + if (wrapInsn.getType() == InsnType.CHECK_CAST) { + wrapInsn.add(AFlag.EXPLICIT_CAST); + } + } + } + } + } + } + + private void resolveTypeVars(IMethodDetails mthDetails, Map typeVarsMapping) { + List argTypes = mthDetails.getArgTypes(); + int argsCount = argTypes.size(); + for (int argNum = 0; argNum < argsCount; argNum++) { + ArgType argType = argTypes.get(argNum); + if (argType == null) { + throw new JadxRuntimeException("Null arg type in " + mthDetails + " at: " + argNum + " in: " + argTypes); + } + if (argType.containsTypeVariable()) { + ArgType resolvedType = root.getTypeUtils().replaceTypeVariablesUsingMap(argType, typeVarsMapping); + if (resolvedType == null || resolvedType.containsTypeVariable()) { + // type variables erased from method info by compiler + resolvedType = mthDetails.getMethodInfo().getArgumentsTypes().get(argNum); + } + argTypes.set(argNum, resolvedType); + } + } + } + + private List searchCastTypes(MethodNode parentMth, IMethodDetails mthDetails, List overloadedMethods, + List compilerVarTypes) { + // try compile types + if (isOverloadResolved(mthDetails, overloadedMethods, compilerVarTypes)) { + return compilerVarTypes; + } + int argsCount = compilerVarTypes.size(); + List castTypes = new ArrayList<>(compilerVarTypes); + + // replace unknown types + boolean changed = replaceUnknownTypes(castTypes, mthDetails.getArgTypes()); + if (changed && isOverloadResolved(mthDetails, overloadedMethods, castTypes)) { + return castTypes; + } + + // replace generic types + changed = false; + for (int i = 0; i < argsCount; i++) { + ArgType castType = castTypes.get(i); + ArgType mthType = mthDetails.getArgTypes().get(i); + if (!castType.isGeneric() && mthType.isGeneric()) { + castTypes.set(i, mthType); + changed = true; + } + } + if (changed && isOverloadResolved(mthDetails, overloadedMethods, castTypes)) { + return castTypes; + } + + // if just one arg => cast will resolve + if (argsCount == 1) { + return mthDetails.getArgTypes(); + } + + // TODO: try to minimize casts count + + parentMth.addComment("JADX DEBUG: Failed to find minimal casts for resolve overloaded methods, cast all args instead" + + "\n method: " + mthDetails + + "\n arg types: " + compilerVarTypes + + "\n candidates:\n " + Utils.listToString(overloadedMethods, "\n ")); + // not resolved -> cast all args + return mthDetails.getArgTypes(); + } + + private boolean replaceUnknownTypes(List castTypes, List mthArgTypes) { + int argsCount = castTypes.size(); + boolean changed = false; + for (int i = 0; i < argsCount; i++) { + ArgType castType = castTypes.get(i); + if (!castType.isTypeKnown()) { + ArgType mthType = mthArgTypes.get(i); + castTypes.set(i, mthType); + changed = true; + } + } + return changed; + } + + private boolean isOverloadResolved(IMethodDetails expectedMthDetails, List overloadedMethods, List castTypes) { + if (overloadedMethods.isEmpty()) { + return false; + } + // TODO: search closest method, instead filtering + List strictMethods = filterApplicableMethods(overloadedMethods, castTypes, MethodInvokeVisitor::isStrictTypes); + if (strictMethods.size() == 1) { + return strictMethods.get(0).equals(expectedMthDetails); + } + List resolvedMethods = filterApplicableMethods(overloadedMethods, castTypes, MethodInvokeVisitor::isTypeApplicable); + if (resolvedMethods.size() == 1) { + return resolvedMethods.get(0).equals(expectedMthDetails); + } + return false; + } + + private static boolean isStrictTypes(TypeCompareEnum result) { + return result.isEqual(); + } + + private static boolean isTypeApplicable(TypeCompareEnum result) { + return result.isNarrowOrEqual() || result == TypeCompareEnum.WIDER_BY_GENERIC; + } + + private List filterApplicableMethods(List methods, List types, + Function acceptFunction) { + List list = new ArrayList<>(methods.size()); + for (IMethodDetails m : methods) { + if (isMethodAcceptable(m, types, acceptFunction)) { + list.add(m); + } + } + return list; + } + + private boolean isMethodAcceptable(IMethodDetails methodDetails, List types, + Function acceptFunction) { + List mthTypes = methodDetails.getArgTypes(); + int argCount = mthTypes.size(); + if (argCount != types.size()) { + return false; + } + TypeCompare typeCompare = root.getTypeUpdate().getTypeCompare(); + for (int i = 0; i < argCount; i++) { + ArgType mthType = mthTypes.get(i); + ArgType argType = types.get(i); + TypeCompareEnum result = typeCompare.compareTypes(argType, mthType); + if (!acceptFunction.apply(result)) { + return false; + } + } + return true; + } + + private List collectCompilerVarTypes(BaseInvokeNode insn, int argOffset) { + int argsCount = insn.getArgsCount(); + List result = new ArrayList<>(argsCount); + for (int i = argOffset; i < argsCount; i++) { + InsnArg arg = insn.getArg(i); + result.add(getCompilerVarType(arg)); + } + return result; + } + + /** + * Return type as seen by compiler + */ + private ArgType getCompilerVarType(InsnArg arg) { + if (arg instanceof LiteralArg) { + LiteralArg literalArg = (LiteralArg) arg; + ArgType type = literalArg.getType(); + if (literalArg.getLiteral() == 0) { + if (type.isObject() || type.isArray()) { + // null + return ArgType.UNKNOWN_OBJECT; + } + } + if (type.isPrimitive() && !arg.contains(AFlag.EXPLICIT_PRIMITIVE_TYPE)) { + return ArgType.INT; + } + return arg.getType(); + } + if (arg instanceof RegisterArg) { + return arg.getType(); + } + if (arg instanceof InsnWrapArg) { + InsnWrapArg wrapArg = (InsnWrapArg) arg; + InsnNode wrapInsn = wrapArg.getWrapInsn(); + if (wrapInsn.getResult() != null) { + return wrapInsn.getResult().getType(); + } + return arg.getType(); + } + throw new JadxRuntimeException("Unknown var type for: " + arg); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 96971ab60..64ee4d9e4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -255,7 +255,7 @@ public class ModVisitor extends AbstractVisitor { private static void removeRedundantCast(MethodNode mth, BlockNode block, int i, IndexInsnNode insn) { InsnArg castArg = insn.getArg(0); ArgType castType = (ArgType) insn.getIndex(); - if (!ArgType.isCastNeeded(mth.dex(), castArg.getType(), castType) + if (!ArgType.isCastNeeded(mth.root(), castArg.getType(), castType) || isCastDuplicate(insn)) { InsnNode insnNode = new InsnNode(InsnType.MOVE, 1); insnNode.setResult(insn.getResult()); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 2e8ae2ddf..33e487345 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -49,7 +49,8 @@ public class SimplifyVisitor extends AbstractVisitor { @Override public void init(RootNode root) { - stringGetBytesMth = MethodInfo.externalMth( + stringGetBytesMth = MethodInfo.fromDetails( + root, ClassInfo.fromType(root, ArgType.STRING), "getBytes", Collections.emptyList(), @@ -224,7 +225,7 @@ public class SimplifyVisitor extends AbstractVisitor { } ArgType castToType = (ArgType) castInsn.getIndex(); - if (!ArgType.isCastNeeded(mth.dex(), argType, castToType) + if (!ArgType.isCastNeeded(mth.root(), argType, castToType) || isCastDuplicate(castInsn)) { InsnNode insnNode = new InsnNode(InsnType.MOVE, 1); insnNode.setOffset(castInsn.getOffset()); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java index ffdd7d220..4e703458e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java @@ -16,7 +16,6 @@ import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.LoopInfo; import jadx.core.dex.instructions.InsnType; -import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -422,7 +421,7 @@ public class BlockProcessor extends AbstractVisitor { } private static boolean mergeConstReturn(MethodNode mth) { - if (mth.getReturnType() == ArgType.VOID) { + if (mth.isVoidReturn()) { return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java index 726aa4a12..561e92cd3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/DebugInfoApplyVisitor.java @@ -90,7 +90,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { RegDebugInfoAttr debugInfo = debugInfoSet.iterator().next(); applyDebugInfo(mth, ssaVar, debugInfo.getRegType(), debugInfo.getName()); } else { - LOG.warn("Multiple debug info for {}: {}", ssaVar, debugInfoSet); + mth.addComment("JADX INFO: Multiple debug info for " + ssaVar + ": " + debugInfoSet); for (RegDebugInfoAttr debugInfo : debugInfoSet) { applyDebugInfo(mth, ssaVar, debugInfo.getRegType(), debugInfo.getName()); } @@ -150,14 +150,6 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { if (NameMapper.isValidAndPrintable(varName)) { ssaVar.setName(varName); } - detachDebugInfo(ssaVar.getAssign()); - ssaVar.getUseList().forEach(DebugInfoApplyVisitor::detachDebugInfo); - } - } - - private static void detachDebugInfo(RegisterArg reg) { - if (reg != null) { - reg.remove(AType.REG_DEBUG_INFO); } } @@ -172,7 +164,7 @@ public class DebugInfoApplyVisitor extends AbstractVisitor { * Fix debug info for splitter 'return' instructions */ private static void fixLinesForReturn(MethodNode mth) { - if (mth.getReturnType().equals(ArgType.VOID)) { + if (mth.isVoidReturn()) { return; } InsnNode origReturn = null; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/LocalVar.java b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/LocalVar.java index f25ca9b64..35a5fa122 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/LocalVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/debuginfo/LocalVar.java @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; +import jadx.core.dex.nodes.parser.SignatureParser; import jadx.core.utils.InsnUtils; public final class LocalVar { @@ -31,7 +32,7 @@ public final class LocalVar { this.name = name; if (sign != null) { try { - ArgType gType = ArgType.parseGenericSignature(sign); + ArgType gType = new SignatureParser(sign).consumeType(); if (checkSignature(type, gType)) { type = gType; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java index dc6b1e3b0..b3e09b379 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java @@ -3,7 +3,6 @@ package jadx.core.dex.visitors.regions; import java.util.List; import jadx.core.dex.attributes.AFlag; -import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; @@ -96,7 +95,7 @@ public class IfRegionVisitor extends AbstractVisitor { } private static void moveReturnToThenBlock(MethodNode mth, IfRegion ifRegion) { - if (!mth.getReturnType().equals(ArgType.VOID) + if (!mth.isVoidReturn() && hasSimpleReturnBlock(ifRegion.getElseRegion()) /* && insnsCount(ifRegion.getThenRegion()) < 2 */) { invertIfRegion(ifRegion); @@ -139,7 +138,7 @@ public class IfRegionVisitor extends AbstractVisitor { // code style check: // will remove 'return;' from 'then' and 'else' with one instruction // see #jadx.tests.integration.conditions.TestConditions9 - if (mth.getReturnType() == ArgType.VOID + if (mth.isVoidReturn() && insnsCount(ifRegion.getThenRegion()) == 2 && insnsCount(ifRegion.getElseRegion()) == 2) { return false; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java index 5fad69e3e..87d3c7598 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.ListIterator; import jadx.core.dex.attributes.AFlag; -import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IBranchRegion; @@ -26,7 +25,7 @@ public class ReturnVisitor extends AbstractVisitor { @Override public void visit(MethodNode mth) throws JadxException { // remove useless returns in void methods - if (mth.getReturnType().equals(ArgType.VOID)) { + if (mth.isVoidReturn()) { DepthRegionTraversal.traverse(mth, new ReturnRemoverVisitor()); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java index 6fc11c4d0..36b0042fd 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java @@ -7,7 +7,6 @@ import java.util.Map; import jadx.core.dex.attributes.AFlag; 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.InsnWrapArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -123,7 +122,7 @@ public class TernaryMod implements IRegionIterativeVisitor { return true; } - if (!mth.getReturnType().equals(ArgType.VOID) + if (!mth.isVoidReturn() && thenInsn.getType() == InsnType.RETURN && elseInsn.getType() == InsnType.RETURN) { InsnArg thenArg = thenInsn.getArg(0); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeAssign.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeAssign.java index ad263a789..b40227a3c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeAssign.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeBoundInvokeAssign.java @@ -4,7 +4,6 @@ import jadx.core.dex.instructions.InvokeNode; 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. @@ -38,7 +37,7 @@ public final class TypeBoundInvokeAssign implements ITypeBoundDynamic { } private ArgType getReturnType(ArgType instanceType) { - ArgType resultGeneric = TypeUtils.replaceClassGenerics(root, instanceType, genericReturnType); + ArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(instanceType, genericReturnType); if (resultGeneric != null) { return resultGeneric; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompare.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompare.java index ad1239fe3..3e7905695 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompare.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompare.java @@ -8,11 +8,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.ArgType.WildcardBound; import jadx.core.dex.instructions.args.PrimitiveType; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxRuntimeException; import static jadx.core.dex.visitors.typeinference.TypeCompareEnum.CONFLICT; +import static jadx.core.dex.visitors.typeinference.TypeCompareEnum.EQUAL; import static jadx.core.dex.visitors.typeinference.TypeCompareEnum.NARROW; import static jadx.core.dex.visitors.typeinference.TypeCompareEnum.NARROW_BY_GENERIC; import static jadx.core.dex.visitors.typeinference.TypeCompareEnum.UNKNOWN; @@ -143,18 +145,42 @@ public class TypeCompare { if (firstGenericType && secondGenericType && !objectsEquals) { return CONFLICT; } + boolean firstGeneric = first.isGeneric(); + boolean secondGeneric = second.isGeneric(); + if (firstGenericType || secondGenericType) { + ArgType firstWildcardType = first.getWildcardType(); + ArgType secondWildcardType = second.getWildcardType(); + if (firstWildcardType != null || secondWildcardType != null) { + if (firstWildcardType != null && secondGenericType && first.getWildcardBound() == WildcardBound.UNBOUND) { + return CONFLICT; + } + if (firstGenericType && secondWildcardType != null && second.getWildcardBound() == WildcardBound.UNBOUND) { + return CONFLICT; + } + } if (firstGenericType) { return compareGenericTypeWithObject(first, second); } else { return compareGenericTypeWithObject(second, first).invert(); } } - boolean firstGeneric = first.isGeneric(); - boolean secondGeneric = second.isGeneric(); - if (firstGeneric != secondGeneric && objectsEquals) { - // don't check generics for now - return firstGeneric ? NARROW_BY_GENERIC : WIDER_BY_GENERIC; + if (objectsEquals) { + if (firstGeneric != secondGeneric) { + return firstGeneric ? NARROW_BY_GENERIC : WIDER_BY_GENERIC; + } + // both generics on same object, compare generics arrays + ArgType[] firstGenericTypes = first.getGenericTypes(); + ArgType[] secondGenericTypes = second.getGenericTypes(); + int len = firstGenericTypes.length; + if (len == secondGenericTypes.length) { + for (int i = 0; i < len; i++) { + TypeCompareEnum res = compareTypes(firstGenericTypes[i], secondGenericTypes[i]); + if (res != EQUAL) { + return res; + } + } + } } boolean firstIsObjCls = first.equals(ArgType.OBJECT); if (firstIsObjCls || second.equals(ArgType.OBJECT)) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompareEnum.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompareEnum.java index 1a42c8a32..7af383b2d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompareEnum.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeCompareEnum.java @@ -42,4 +42,12 @@ public enum TypeCompareEnum { public boolean isNarrow() { return this == NARROW || this == NARROW_BY_GENERIC; } + + public boolean isNarrowOrEqual() { + return isEqual() || isNarrow(); + } + + public boolean isGeneric() { + return this == WIDER_BY_GENERIC || this == NARROW_BY_GENERIC; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java index 888aa4039..a86e3f4c7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java @@ -16,7 +16,7 @@ import jadx.core.clsp.ClspGraph; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.info.ClassInfo; -import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; @@ -30,11 +30,13 @@ 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.BlockNode; +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.trycatch.ExcHandlerAttr; import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.dex.visitors.AttachMethodDetails; import jadx.core.dex.visitors.ConstInlineVisitor; import jadx.core.dex.visitors.InitCodeVariables; import jadx.core.dex.visitors.JadxVisitor; @@ -48,7 +50,8 @@ import jadx.core.utils.Utils; desc = "Calculate best types for SSA variables", runAfter = { SSATransform.class, - ConstInlineVisitor.class + ConstInlineVisitor.class, + AttachMethodDetails.class } ) public final class TypeInferenceVisitor extends AbstractVisitor { @@ -68,6 +71,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (mth.isNoCode()) { return; } + if (Consts.DEBUG) { + LOG.info("Start type inference in method: {}", mth); + } boolean resolved = runTypePropagation(mth); if (!resolved) { boolean moveAdded = false; @@ -271,11 +277,10 @@ public final class TypeInferenceVisitor extends AbstractVisitor { } private ITypeBound makeAssignInvokeBound(InvokeNode invokeNode) { - MethodInfo callMth = invokeNode.getCallMth(); - ArgType boundType = callMth.getReturnType(); - ArgType genericReturnType = root.getMethodGenericReturnType(callMth); + ArgType boundType = invokeNode.getCallMth().getReturnType(); + ArgType genericReturnType = root.getMethodUtils().getMethodGenericReturnType(invokeNode); if (genericReturnType != null) { - if (genericReturnType.containsGenericType()) { + if (genericReturnType.containsTypeVariable()) { InvokeType invokeType = invokeNode.getInvokeType(); if (invokeNode.getArgsCount() != 0 && invokeType != InvokeType.STATIC && invokeType != InvokeType.SUPER) { @@ -294,6 +299,15 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (insn == null) { return null; } + if (insn instanceof BaseInvokeNode) { + IMethodDetails methodDetails = root.getMethodUtils().getMethodDetails((BaseInvokeNode) insn); + if (methodDetails != null) { + if (methodDetails.getArgTypes().stream().anyMatch(ArgType::containsTypeVariable)) { + // don't add const bound for generic type variables + return null; + } + } + } return new TypeBoundConst(BoundEnum.USE, regArg.getInitType(), regArg); } 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 636293a9c..94149629b 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,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.Consts; -import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.ArgType; @@ -22,7 +21,6 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; 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; @@ -79,8 +77,9 @@ public final class TypeUpdate { return SAME; } if (Consts.DEBUG) { - LOG.debug("Applying types, init for {} -> {}", ssaVar, candidateType); - updates.forEach(updateEntry -> LOG.debug(" {} -> {}", updateEntry.getType(), updateEntry.getArg())); + LOG.debug("Applying types for {} -> {}", ssaVar, candidateType); + updates.forEach(updateEntry -> LOG.debug(" {} -> {}, insn: {}", + updateEntry.getType(), updateEntry.getArg(), updateEntry.getArg().getParentInsn())); } updateInfo.applyUpdates(); return CHANGED; @@ -282,18 +281,17 @@ public final class TypeUpdate { if (insn.getResult() == null) { return SAME; } - if (candidateType.isGeneric() || candidateType.isGenericType()) { + if (candidateType.containsTypeVariable()) { InvokeNode invokeNode = (InvokeNode) insn; - MethodInfo callMth = invokeNode.getCallMth(); if (isAssign(insn, arg)) { // TODO: implement backward type propagation (from result to instance) return SAME; } else { - ArgType returnType = root.getMethodGenericReturnType(callMth); + ArgType returnType = root.getMethodUtils().getMethodGenericReturnType(invokeNode); if (returnType == null) { return SAME; } - ArgType resultGeneric = TypeUtils.replaceClassGenerics(root, candidateType, returnType); + ArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(candidateType, returnType); if (resultGeneric == null) { return SAME; } diff --git a/jadx-core/src/main/java/jadx/core/utils/TypeUtils.java b/jadx-core/src/main/java/jadx/core/utils/TypeUtils.java deleted file mode 100644 index 13a35bb9b..000000000 --- a/jadx-core/src/main/java/jadx/core/utils/TypeUtils.java +++ /dev/null @@ -1,119 +0,0 @@ -package jadx.core.utils; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.jetbrains.annotations.Nullable; - -import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.instructions.CallMthInterface; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.nodes.GenericInfo; -import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.exceptions.JadxRuntimeException; - -public class TypeUtils { - /** - * Replace generic types in {@code typeWithGeneric} using instance types - *
- * Example: - *
    - *
  • {@code instanceType: Set} - *
  • {@code typeWithGeneric: Iterator} - *
  • {@code return: Iterator} - *
- */ - @Nullable - public static ArgType replaceClassGenerics(RootNode root, ArgType instanceType, ArgType typeWithGeneric) { - if (typeWithGeneric == null) { - return null; - } - if (instanceType.isGeneric()) { - List generics = root.getClassGenerics(instanceType); - if (generics.isEmpty()) { - return null; - } - ArgType[] actualTypes = instanceType.getGenericTypes(); - if (actualTypes == null) { - return null; - } - int genericParamsCount = actualTypes.length; - if (genericParamsCount != generics.size()) { - return null; - } - Map replaceMap = new HashMap<>(genericParamsCount); - for (int i = 0; i < genericParamsCount; i++) { - ArgType actualType = actualTypes[i]; - ArgType genericType = generics.get(i).getGenericType(); - replaceMap.put(genericType, actualType); - } - return replaceGenericUsingTypeMap(typeWithGeneric, replaceMap); - } - return null; - } - - @Nullable - public static ArgType replaceMethodGenerics(RootNode root, InsnNode invokeInsn, ArgType typeWithGeneric) { - if (typeWithGeneric == null) { - return null; - } - if (!(invokeInsn instanceof CallMthInterface)) { - throw new JadxRuntimeException("Expected CallMthInterface, got: " + invokeInsn.getClass()); - } - CallMthInterface callInsn = (CallMthInterface) invokeInsn; - MethodInfo mthInfo = callInsn.getCallMth(); - List methodArgTypes = root.getMethodArgTypes(mthInfo); - if (methodArgTypes.isEmpty()) { - return null; - } - int firstArgOffset = callInsn.getFirstArgOffset(); - int argsCount = methodArgTypes.size(); - for (int i = 0; i < argsCount; i++) { - ArgType methodArgType = methodArgTypes.get(i); - InsnArg insnArg = invokeInsn.getArg(i + firstArgOffset); - ArgType insnType = insnArg.getType(); - if (methodArgType.equals(typeWithGeneric)) { - return insnType; - } - } - // TODO build complete map for type variables - return null; - } - - private static ArgType replaceGenericUsingTypeMap(ArgType replaceType, Map replaceMap) { - if (replaceType.isGenericType()) { - return replaceMap.get(replaceType); - } - - ArgType wildcardType = replaceType.getWildcardType(); - if (wildcardType != null && wildcardType.containsGenericType()) { - ArgType newWildcardType = replaceGenericUsingTypeMap(wildcardType, replaceMap); - if (newWildcardType == null) { - return null; - } - return ArgType.wildcard(newWildcardType, replaceType.getWildcardBound()); - } - - 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 = replaceGenericUsingTypeMap(genericType, replaceMap); - if (type == null) { - type = genericType; - } - newTypes[i] = type; - } - return ArgType.generic(replaceType.getObject(), newTypes); - } - return null; - } - - private TypeUtils() { - } -} diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index a775df368..daf73e7e8 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -247,6 +247,13 @@ public class Utils { return list.get(list.size() - 1); } + public static T getOrElse(@Nullable T obj, T defaultObj) { + if (obj == null) { + return defaultObj; + } + return obj; + } + public static boolean isEmpty(Collection col) { return col == null || col.isEmpty(); } diff --git a/jadx-core/src/test/java/jadx/core/dex/instructions/args/ArgTypeTest.java b/jadx-core/src/test/java/jadx/core/dex/instructions/args/ArgTypeTest.java index fbed8cad4..8a70a8e2d 100644 --- a/jadx-core/src/test/java/jadx/core/dex/instructions/args/ArgTypeTest.java +++ b/jadx-core/src/test/java/jadx/core/dex/instructions/args/ArgTypeTest.java @@ -24,10 +24,10 @@ class ArgTypeTest { @Test void testContainsGenericType() { ArgType wildcard = ArgType.wildcard(ArgType.genericType("T"), ArgType.WildcardBound.SUPER); - assertTrue(wildcard.containsGenericType()); + assertTrue(wildcard.containsTypeVariable()); ArgType type = ArgType.generic("java.lang.List", wildcard); - assertTrue(type.containsGenericType()); + assertTrue(type.containsTypeVariable()); } @Test diff --git a/jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java b/jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java index f96c01b69..7465b6742 100644 --- a/jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java +++ b/jadx-core/src/test/java/jadx/core/dex/visitors/typeinference/TypeCompareTest.java @@ -5,6 +5,8 @@ import java.util.Collections; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import jadx.NotYetImplemented; import jadx.NotYetImplementedExtension; @@ -14,19 +16,24 @@ import jadx.core.dex.nodes.RootNode; import static jadx.core.dex.instructions.args.ArgType.BOOLEAN; import static jadx.core.dex.instructions.args.ArgType.CHAR; +import static jadx.core.dex.instructions.args.ArgType.CLASS; import static jadx.core.dex.instructions.args.ArgType.INT; import static jadx.core.dex.instructions.args.ArgType.NARROW; import static jadx.core.dex.instructions.args.ArgType.NARROW_INTEGRAL; import static jadx.core.dex.instructions.args.ArgType.OBJECT; +import static jadx.core.dex.instructions.args.ArgType.STRING; import static jadx.core.dex.instructions.args.ArgType.UNKNOWN; import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_ARRAY; import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_OBJECT; import static jadx.core.dex.instructions.args.ArgType.array; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static jadx.core.dex.instructions.args.ArgType.generic; +import static jadx.core.dex.instructions.args.ArgType.wildcard; +import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(NotYetImplementedExtension.class) public class TypeCompareTest { + private static final Logger LOG = LoggerFactory.getLogger(TypeCompareTest.class); + private TypeCompare compare; @BeforeEach @@ -65,8 +72,21 @@ public class TypeCompareTest { firstIsNarrow(array(OBJECT), OBJECT); firstIsNarrow(array(OBJECT), array(UNKNOWN_OBJECT)); + firstIsNarrow(array(STRING), array(UNKNOWN_OBJECT)); + firstIsNarrow(array(STRING), array(OBJECT)); firstIsNarrow(UNKNOWN_ARRAY, OBJECT); + + check(array(OBJECT), array(INT), TypeCompareEnum.CONFLICT); + + ArgType integerType = ArgType.object("java.lang.Integer"); + check(array(OBJECT), array(integerType), TypeCompareEnum.WIDER); + check(array(INT), array(integerType), TypeCompareEnum.CONFLICT); + check(array(INT), array(INT), TypeCompareEnum.EQUAL); + + ArgType wildClass = generic(CLASS, wildcard()); + check(array(wildClass), array(CLASS), TypeCompareEnum.NARROW_BY_GENERIC); + check(array(CLASS), array(wildClass), TypeCompareEnum.WIDER_BY_GENERIC); } @Test @@ -78,10 +98,15 @@ public class TypeCompareTest { ArgType valueType = ArgType.genericType("V"); ArgType mapGeneric = ArgType.generic(mapCls.getObject(), keyType, valueType); - check(mapGeneric, mapCls, TypeCompareEnum.NARROW_BY_GENERIC); check(mapCls, mapGeneric, TypeCompareEnum.WIDER_BY_GENERIC); - check(mapCls, setCls, TypeCompareEnum.CONFLICT); + + ArgType setGeneric = ArgType.generic(setCls.getObject(), valueType); + ArgType setWildcard = ArgType.generic(setCls.getObject(), ArgType.wildcard()); + + check(setWildcard, setGeneric, TypeCompareEnum.CONFLICT); + check(setWildcard, setCls, TypeCompareEnum.NARROW_BY_GENERIC); + // TODO implement compare for wildcard with bounds } @Test @@ -99,10 +124,7 @@ public class TypeCompareTest { tType.setExtendTypes(Collections.singletonList(ArgType.STRING)); check(tType, ArgType.STRING, TypeCompareEnum.NARROW_BY_GENERIC); - check(ArgType.STRING, tType, TypeCompareEnum.WIDER_BY_GENERIC); - check(tType, ArgType.OBJECT, TypeCompareEnum.NARROW_BY_GENERIC); - check(ArgType.OBJECT, tType, TypeCompareEnum.WIDER_BY_GENERIC); } @Test @@ -111,18 +133,21 @@ public class TypeCompareTest { ArgType vType = ArgType.genericType("V"); // TODO: use extend types from generic declaration for more strict checks check(vType, ArgType.STRING, TypeCompareEnum.CONFLICT); - check(ArgType.STRING, vType, TypeCompareEnum.CONFLICT); } private void firstIsNarrow(ArgType first, ArgType second) { check(first, second, TypeCompareEnum.NARROW); - // reverse - check(second, first, TypeCompareEnum.WIDER); } private void check(ArgType first, ArgType second, TypeCompareEnum expectedResult) { - TypeCompareEnum result = compare.compareTypes(first, second); - assertThat("Compare '" + first + "' vs '" + second + '\'', - result, is(expectedResult)); + LOG.debug("Compare: '{}' and '{}', expect: '{}'", first, second, expectedResult); + + assertThat(compare.compareTypes(first, second)) + .as("Compare '%s' and '%s'", first, second) + .isEqualTo(expectedResult); + + assertThat(compare.compareTypes(second, first)) + .as("Compare '%s' and '%s'", second, first) + .isEqualTo(expectedResult.invert()); } } diff --git a/jadx-core/src/test/java/jadx/core/utils/TypeUtilsTest.java b/jadx-core/src/test/java/jadx/core/utils/TypeUtilsTest.java index c04cce9ab..ce1ae066e 100644 --- a/jadx-core/src/test/java/jadx/core/utils/TypeUtilsTest.java +++ b/jadx-core/src/test/java/jadx/core/utils/TypeUtilsTest.java @@ -1,6 +1,5 @@ package jadx.core.utils; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeAll; @@ -10,7 +9,7 @@ import org.slf4j.LoggerFactory; import jadx.api.JadxArgs; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.nodes.GenericInfo; +import jadx.core.dex.nodes.GenericTypeParameter; import jadx.core.dex.nodes.RootNode; import static org.hamcrest.MatcherAssert.assertThat; @@ -25,17 +24,16 @@ class TypeUtilsTest { @BeforeAll public static void init() { root = new RootNode(new JadxArgs()); - root.load(Collections.emptyList()); root.initClassPath(); } @Test public void testReplaceGenericsWithWildcards() { // check classpath graph - List classGenerics = root.getClassGenerics(ArgType.object("java.util.ArrayList")); + List classGenerics = root.getTypeUtils().getClassGenerics(ArgType.object("java.util.ArrayList")); assertThat(classGenerics, hasSize(1)); - GenericInfo genericInfo = classGenerics.get(0); - assertThat(genericInfo.getGenericType(), is(ArgType.genericType("E"))); + GenericTypeParameter genericInfo = classGenerics.get(0); + assertThat(genericInfo.getTypeVariable(), is(ArgType.genericType("E"))); assertThat(genericInfo.getExtendsList(), hasSize(0)); // prepare input @@ -46,7 +44,7 @@ class TypeUtilsTest { LOG.debug("generic: {}", generic); // replace - ArgType result = TypeUtils.replaceClassGenerics(root, instanceType, generic); + ArgType result = root.getTypeUtils().replaceClassGenerics(instanceType, generic); LOG.debug("result: {}", result); ArgType expected = ArgType.generic("java.util.List", ArgType.wildcard(ArgType.OBJECT, ArgType.WildcardBound.SUPER)); diff --git a/jadx-core/src/test/java/jadx/tests/functional/JadxClasspathTest.java b/jadx-core/src/test/java/jadx/tests/functional/JadxClasspathTest.java index e9911980e..8437c6e3f 100644 --- a/jadx-core/src/test/java/jadx/tests/functional/JadxClasspathTest.java +++ b/jadx-core/src/test/java/jadx/tests/functional/JadxClasspathTest.java @@ -1,39 +1,34 @@ package jadx.tests.functional; -import java.io.IOException; +import java.util.Collections; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import jadx.api.JadxArgs; import jadx.core.clsp.ClspGraph; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.exceptions.DecodeException; import static jadx.core.dex.instructions.args.ArgType.STRING; import static jadx.core.dex.instructions.args.ArgType.object; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class JadxClasspathTest { private static final String JAVA_LANG_EXCEPTION = "java.lang.Exception"; private static final String JAVA_LANG_THROWABLE = "java.lang.Throwable"; - private DexNode dex; + private RootNode root; private ClspGraph clsp; @BeforeEach - public void initClsp() throws IOException, DecodeException { - clsp = new ClspGraph(); - clsp.load(); - dex = mock(DexNode.class); - RootNode rootNode = mock(RootNode.class); - when(rootNode.getClsp()).thenReturn(clsp); - when(dex.root()).thenReturn(rootNode); + public void initClsp() { + this.root = new RootNode(new JadxArgs()); + this.root.load(Collections.emptyList()); + this.root.initClassPath(); + this.clsp = root.getClsp(); } @Test @@ -44,9 +39,9 @@ public class JadxClasspathTest { assertTrue(clsp.isImplements(JAVA_LANG_EXCEPTION, JAVA_LANG_THROWABLE)); assertFalse(clsp.isImplements(JAVA_LANG_THROWABLE, JAVA_LANG_EXCEPTION)); - assertFalse(ArgType.isCastNeeded(dex, objExc, objThr)); - assertTrue(ArgType.isCastNeeded(dex, objThr, objExc)); + assertFalse(ArgType.isCastNeeded(root, objExc, objThr)); + assertTrue(ArgType.isCastNeeded(root, objThr, objExc)); - assertTrue(ArgType.isCastNeeded(dex, ArgType.OBJECT, STRING)); + assertTrue(ArgType.isCastNeeded(root, ArgType.OBJECT, STRING)); } } diff --git a/jadx-core/src/test/java/jadx/tests/functional/SignatureParserTest.java b/jadx-core/src/test/java/jadx/tests/functional/SignatureParserTest.java index 116023bea..c724aa9cc 100644 --- a/jadx-core/src/test/java/jadx/tests/functional/SignatureParserTest.java +++ b/jadx-core/src/test/java/jadx/tests/functional/SignatureParserTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType.WildcardBound; -import jadx.core.dex.nodes.GenericInfo; +import jadx.core.dex.nodes.GenericTypeParameter; import jadx.core.dex.nodes.parser.SignatureParser; import static jadx.core.dex.instructions.args.ArgType.INT; @@ -92,12 +92,12 @@ class SignatureParserTest { @SuppressWarnings("unchecked") private static void checkGenerics(String g, Object... objs) { - List genericsList = new SignatureParser(g).consumeGenericMap(); - List expectedList = new ArrayList<>(); + List genericsList = new SignatureParser(g).consumeGenericTypeParameters(); + List expectedList = new ArrayList<>(); for (int i = 0; i < objs.length; i += 2) { ArgType generic = genericType((String) objs[i]); List list = (List) objs[i + 1]; - expectedList.add(new GenericInfo(generic, list)); + expectedList.add(new GenericTypeParameter(generic, list)); } assertThat(genericsList, is(expectedList)); } @@ -122,7 +122,7 @@ class SignatureParserTest { @Test public void testBadGenericMap() { - List list = new SignatureParser(" list = new SignatureParser(") null); + call((ArrayList) null); + } + public void call(String str) { c += 1; } public void call(List list) { - c += 2; + c += 10; } public void call(ArrayList list) { - c += 4; + c += 100; } public void check() { test(); - assertThat(c, is(2 + 4)); + assertThat(c, is(10 + 100)); c = 0; test2("str"); assertThat(c, is(1)); + c = 0; + test3(); + assertThat(c, is(111)); } } @@ -63,7 +72,7 @@ public class TestCastInOverloadedInvoke extends IntegrationTest { @NotYetImplemented @Test - public void test2() { + public void testNYI() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); diff --git a/jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke3.java b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke3.java new file mode 100644 index 000000000..f60353456 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestCastInOverloadedInvoke3.java @@ -0,0 +1,41 @@ +package jadx.tests.integration.invoke; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +/** + * Test cast for 'unknown' but overloaded method + */ +public class TestCastInOverloadedInvoke3 extends IntegrationTest { + + public static class OuterCls { + static int c = 0; + + public static void call(String str) { + c = 1; + } + + public static void call(List list) { + c = 10; + } + } + + public static class TestCls { + public void test() { + OuterCls.call((String) null); + } + } + + @Test + public void test() { + disableCompilation(); + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("OuterCls.call((String) null);"); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java new file mode 100644 index 000000000..7569de41a --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestHierarchyOverloadedInvoke.java @@ -0,0 +1,100 @@ +package jadx.tests.integration.invoke; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.NotYetImplemented; +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class TestHierarchyOverloadedInvoke extends IntegrationTest { + + public static class TestCls { + static int c = 0; + B b = new B(); + + public interface I { + default void call(String str) { + c += 1; + } + } + + public static class A implements I { + public void call(List list) { + c += 10; + } + } + + public static class B extends A { + public void call(ArrayList list) { + c += 100; + } + } + + public void test() { + b.call(new ArrayList<>()); + b.call((List) new ArrayList()); + } + + public void test2(Object obj) { + if (obj instanceof String) { + b.call((String) obj); + } + } + + public void test3() { + b.call((String) null); + b.call((List) null); + b.call((ArrayList) null); + } + + public void test4() { + ((I) b).call(null); + ((A) b).call((String) null); + ((A) b).call((List) null); + } + + public void check() { + test(); + assertThat(c, is(10 + 100)); + + c = 0; + test2("str"); + assertThat(c, is(1)); + + c = 0; + test3(); + assertThat(c, is(111)); + + c = 0; + test4(); + assertThat(c, is(12)); + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("b.call((ArrayList) new ArrayList());")); + assertThat(code, containsOne("b.call((List) new ArrayList());")); + + assertThat(code, containsOne("b.call((String) obj);")); + } + + @NotYetImplemented + @Test + public void test2() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("b.call(new ArrayList<>());")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeWithGenerics.java b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeWithGenerics.java index f7693bc21..dd6f96e58 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeWithGenerics.java +++ b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeWithGenerics.java @@ -12,7 +12,7 @@ public class TestSuperInvokeWithGenerics extends IntegrationTest { public static class TestCls { - public class A { + public static class A { public A(T t) { System.out.println("t" + t); } @@ -22,7 +22,7 @@ public class TestSuperInvokeWithGenerics extends IntegrationTest { } } - public class B extends A { + public static class B extends A { public B(String s) { super(s); } diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing2.java index 7bdd21745..91066c011 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestDeboxing2.java @@ -39,7 +39,7 @@ public class TestDeboxing2 extends IntegrationTest { assertThat(code, containsOne("l = 0L;")); // checks for 'check' method - assertThat(code, containsOne("test((Long) null)")); // TODO: cast not needed + assertThat(code, containsOne("test(null)")); assertThat(code, containsOne("test(0L)")); assertThat(code, countString(2, "is(0L)")); assertThat(code, containsOne("test(7L)")); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestLoopInTry2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestLoopInTry2.java index 24e789b24..9942167eb 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestLoopInTry2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestLoopInTry2.java @@ -45,7 +45,7 @@ public class TestLoopInTry2 extends IntegrationTest { assertThat(code, containsOne("try {")); assertThat(code, containsOne("while (in.hasMore()) {")); - assertThat(code, containsOne("decoded[in.cursor()] = DecodedInstruction.decode(in);")); + assertThat(code, containsOne("decoded[in.cursor()] = DecodedInstruction.decode(")); assertThat(code, containsOne("} catch (EOFException e) {")); assertThat(code, containsOne("throw new DecodeException")); } diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination2.java index d478c0309..bab664d8a 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestStringBuilderElimination2.java @@ -27,7 +27,7 @@ public class TestStringBuilderElimination2 extends IntegrationTest { public void test1() { ClassNode cls = getClassNode(TestStringBuilderElimination2.TestCls1.class); String code = cls.getCode().toString(); - assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 2 + 0 + 1.0f + 2.0d + true;")); + assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 2 + 0L + 1.0f + 2.0d + true;")); } public static class TestCls2 { @@ -49,7 +49,7 @@ public class TestStringBuilderElimination2 extends IntegrationTest { public void test2() { ClassNode cls = getClassNode(TestStringBuilderElimination2.TestCls2.class); String code = cls.getCode().toString(); - assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 1 + 2 + 1.0f + 2.0d + true;")); + assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 1 + 2L + 1.0f + 2.0d + true;")); } public static class TestClsStringUtilsReverse { diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally2.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally2.java index eceb4f2c5..7a2966302 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchFinally2.java @@ -6,7 +6,8 @@ import java.io.OutputStream; import org.junit.jupiter.api.Test; -import jadx.core.clsp.NClass; +import jadx.core.clsp.ClspClass; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; import jadx.tests.api.IntegrationTest; @@ -16,21 +17,21 @@ import static org.hamcrest.MatcherAssert.assertThat; public class TestTryCatchFinally2 extends IntegrationTest { public static class TestCls { - private NClass[] classes; + private ClspClass[] classes; public void test(OutputStream output) throws IOException { DataOutputStream out = new DataOutputStream(output); try { out.writeByte(1); out.writeInt(classes.length); - for (NClass cls : classes) { + for (ClspClass cls : classes) { writeString(out, cls.getName()); } - for (NClass cls : classes) { - NClass[] parents = cls.getParents(); + for (ClspClass cls : classes) { + ArgType[] parents = cls.getParents(); out.writeByte(parents.length); - for (NClass parent : parents) { - out.writeInt(parent.getId()); + for (ArgType parent : parents) { + out.writeInt(parent.getObject().hashCode()); } } } finally { @@ -50,9 +51,9 @@ public class TestTryCatchFinally2 extends IntegrationTest { assertThat(code, containsOne("} finally {")); assertThat(code, containsOne("out.close();")); - assertThat(code, containsOne("for (NClass parent : parents) {")); + assertThat(code, containsOne("for (ArgType parent : parents) {")); - assertThat(code, containsOne("for (NClass cls : this.classes) {")); - assertThat(code, containsOne("for (NClass cls2 : this.classes) {")); + assertThat(code, containsOne("for (ClspClass cls : this.classes) {")); + assertThat(code, containsOne("for (ClspClass cls2 : this.classes) {")); } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics2.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics2.java index 6d1fb65f1..d6e529026 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics2.java @@ -32,5 +32,6 @@ public class TestGenerics2 extends SmaliTest { assertThat(code, containsOne("Entry next")); assertThat(code, containsOne("useInt(next.getKey().intValue());")); // no Integer cast + assertThat(code, containsOne("next.getValue().trim();")); // no String cast } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics4.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics4.java index 7ed964d60..35d5dd4fb 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics4.java +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestGenerics4.java @@ -51,7 +51,7 @@ public class TestGenerics4 extends IntegrationTest { @NotYetImplemented @Test - public void testNYI() { + public void testOmitCast() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver12.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver12.java index 8b0c63db1..078b85985 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver12.java +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver12.java @@ -4,7 +4,6 @@ import java.lang.ref.WeakReference; import org.junit.jupiter.api.Test; -import jadx.NotYetImplemented; import jadx.tests.api.IntegrationTest; import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; @@ -31,13 +30,12 @@ public class TestTypeResolver12 extends IntegrationTest { .containsOne("T obj = this.ref.get();"); } - @NotYetImplemented("Generic type inference") @Test public void testNoDebug() { noDebugInfo(); assertThat(getClassNode(TestCls.class)) .code() .doesNotContain("Object obj") - .containsOne("T obj = this.ref.get();"); + .containsOne("T t = this.ref.get();"); } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver13.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver13.java new file mode 100644 index 000000000..ae40038e5 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver13.java @@ -0,0 +1,41 @@ +package jadx.tests.integration.types; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import jadx.NotYetImplemented; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestTypeResolver13 extends IntegrationTest { + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + public static class TestCls { + private static final Set CONST = new HashSet<>(); + private Map, List> map = new HashMap<>(); + + @SuppressWarnings("unchecked") + public List test(Set type) { + List obj = this.map.get(type == null ? CONST : type); + if (obj != null) { + return (List) obj; + } + return null; + } + } + + @NotYetImplemented("additional cast for generic types") + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("public List test(Set type) {") + .containsOne("return (List) obj;"); + } +}