diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index e28931887..d0d39c593 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -33,6 +33,8 @@ import jadx.api.plugins.input.data.ILoadResult; import jadx.api.plugins.options.JadxPluginOptions; import jadx.core.Jadx; import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.nodes.InlinedAttr; import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; @@ -483,12 +485,12 @@ public final class JadxDecompiler implements Closeable { if (parentClass.contains(AFlag.DONT_GENERATE)) { return null; } - if (parentClass != cls) { - JavaClass parentJavaClass = classesMap.get(parentClass); - if (parentJavaClass == null) { - getClasses(); - parentJavaClass = classesMap.get(parentClass); - } + JavaClass parentJavaClass = classesMap.get(parentClass); + if (parentJavaClass == null) { + getClasses(); + parentJavaClass = classesMap.get(parentClass); + } + if (parentJavaClass != null) { loadJavaClass(parentJavaClass); javaClass = classesMap.get(cls); if (javaClass != null) { @@ -512,7 +514,9 @@ public final class JadxDecompiler implements Closeable { return null; } // parent class not loaded yet - JavaClass javaClass = getJavaClassByNode(mth.getParentClass().getTopParentClass()); + ClassNode parentClass = mth.getParentClass(); + ClassNode codeCls = getCodeParentClass(parentClass); + JavaClass javaClass = getJavaClassByNode(codeCls); if (javaClass == null) { return null; } @@ -521,12 +525,26 @@ public final class JadxDecompiler implements Closeable { if (javaMethod != null) { return javaMethod; } - if (mth.getParentClass().hasNotGeneratedParent()) { + if (parentClass.hasNotGeneratedParent()) { return null; } throw new JadxRuntimeException("JavaMethod not found by MethodNode: " + mth); } + private ClassNode getCodeParentClass(ClassNode cls) { + ClassNode codeCls; + InlinedAttr inlinedAttr = cls.get(AType.INLINED); + if (inlinedAttr != null) { + codeCls = inlinedAttr.getInlineCls().getTopParentClass(); + } else { + codeCls = cls.getTopParentClass(); + } + if (codeCls == cls) { + return codeCls; + } + return getCodeParentClass(codeCls); + } + @Nullable private JavaField getJavaFieldByNode(FieldNode fld) { JavaField javaField = fieldsMap.get(fld); diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index 2f0dce9cb..4a6bd0466 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -2,9 +2,12 @@ package jadx.api; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import org.jetbrains.annotations.ApiStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; @@ -14,6 +17,7 @@ import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.Utils; public final class JavaMethod implements JavaNode { + private static final Logger LOG = LoggerFactory.getLogger(JavaMethod.class); private final MethodNode mth; private final JavaClass parent; @@ -73,7 +77,14 @@ public final class JavaMethod implements JavaNode { } JadxDecompiler decompiler = getDeclaringClass().getRootDecompiler(); return ovrdAttr.getRelatedMthNodes().stream() - .map(m -> ((JavaMethod) decompiler.convertNode(m))) + .map(m -> { + JavaMethod javaMth = (JavaMethod) decompiler.convertNode(m); + if (javaMth == null) { + LOG.warn("Failed convert to java method: {}", m); + } + return javaMth; + }) + .filter(Objects::nonNull) .collect(Collectors.toList()); } 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 d62a157fa..25968061b 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 @@ -11,6 +11,7 @@ import jadx.core.dex.attributes.nodes.EnumMapAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.ForceReturnAttr; import jadx.core.dex.attributes.nodes.GenericInfoAttr; +import jadx.core.dex.attributes.nodes.InlinedAttr; import jadx.core.dex.attributes.nodes.JadxCommentsAttr; import jadx.core.dex.attributes.nodes.JadxError; import jadx.core.dex.attributes.nodes.JumpInfo; @@ -55,6 +56,7 @@ public final class AType implements IJadxAttrType { public static final AType ENUM_MAP = new AType<>(); public static final AType CLASS_TYPE_VARS = new AType<>(); public static final AType ANONYMOUS_CLASS = new AType<>(); + public static final AType INLINED = new AType<>(); // field public static final AType FIELD_INIT_INSN = new AType<>(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/InlinedAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/InlinedAttr.java new file mode 100644 index 000000000..67cc88ada --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/InlinedAttr.java @@ -0,0 +1,29 @@ +package jadx.core.dex.attributes.nodes; + +import jadx.api.plugins.input.data.attributes.IJadxAttrType; +import jadx.api.plugins.input.data.attributes.IJadxAttribute; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.nodes.ClassNode; + +public class InlinedAttr implements IJadxAttribute { + + private final ClassNode inlineCls; + + public InlinedAttr(ClassNode inlineCls) { + this.inlineCls = inlineCls; + } + + public ClassNode getInlineCls() { + return inlineCls; + } + + @Override + public IJadxAttrType getAttrType() { + return AType.INLINED; + } + + @Override + public String toString() { + return "INLINED: " + inlineCls; + } +} 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 610a66aca..79387204e 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 @@ -36,6 +36,7 @@ import jadx.core.Consts; import jadx.core.ProcessClass; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.nodes.InlinedAttr; import jadx.core.dex.attributes.nodes.NotificationAttrNode; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo.AFType; @@ -633,6 +634,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN if (inlinedClasses.isEmpty()) { inlinedClasses = new ArrayList<>(5); } + cls.addAttr(new InlinedAttr(this)); inlinedClasses.add(cls); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java index 499d7e760..5b1b89e02 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java @@ -73,6 +73,7 @@ public class ProcessAnonymous extends AbstractVisitor { return; } ClassNode outerCls = anonymousConstructor.getUseIn().get(0).getParentClass(); + outerCls.addInlinedClass(cls); cls.addAttr(new AnonymousClassAttr(outerCls, baseType)); cls.add(AFlag.DONT_GENERATE); anonymousConstructor.add(AFlag.ANONYMOUS_CONSTRUCTOR); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java index 9cba72b4d..a2d069245 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java @@ -74,8 +74,11 @@ public class UsageDialog extends CommonSearchDialog { private List getMethodUseIn() { if (node instanceof JMethod) { JavaMethod method = ((JMethod) node).getJavaMethod(); - if (null != method.getMethodNode().get(AType.METHOD_OVERRIDE)) { - return method.getOverrideRelatedMethods().stream().flatMap(m -> m.getUseIn().stream()).collect(Collectors.toList()); + if (method.getMethodNode().contains(AType.METHOD_OVERRIDE)) { + return method.getOverrideRelatedMethods() + .stream() + .flatMap(m -> m.getUseIn().stream()) + .collect(Collectors.toList()); } } return node.getJavaNode().getUseIn();