Skip to content

Commit

Permalink
fix: ignore anonymous classes in enclosing node search (#1580)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Jul 19, 2022
1 parent c27f2ba commit f4b3645
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ enum AnnType {
VAR,
VAR_REF,
DECLARATION,
OFFSET
OFFSET,
END // class or method body end
}

AnnType getAnnType();
Expand Down
21 changes: 21 additions & 0 deletions jadx-core/src/main/java/jadx/api/metadata/annotations/NodeEnd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package jadx.api.metadata.annotations;

import jadx.api.metadata.ICodeAnnotation;

public class NodeEnd implements ICodeAnnotation {

public static final NodeEnd VALUE = new NodeEnd();

private NodeEnd() {
}

@Override
public AnnType getAnnType() {
return AnnType.END;
}

@Override
public String toString() {
return "END";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.stream.Stream;

import org.jetbrains.annotations.Nullable;

import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeAnnotation.AnnType;
import jadx.api.metadata.ICodeMetadata;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.NodeDeclareRef;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.Utils;

public class CodeMetadataStorage implements ICodeMetadata {
Expand Down Expand Up @@ -55,7 +53,7 @@ public ICodeAnnotation getAt(int position) {
}

@Override
public @Nullable ICodeAnnotation searchUp(int position, ICodeAnnotation.AnnType annType) {
public @Nullable ICodeAnnotation searchUp(int position, AnnType annType) {
for (ICodeAnnotation v : navMap.tailMap(position, true).values()) {
if (v.getAnnType() == annType) {
return v;
Expand All @@ -65,7 +63,7 @@ public ICodeAnnotation getAt(int position) {
}

@Override
public @Nullable ICodeAnnotation searchUp(int position, int limitPos, ICodeAnnotation.AnnType annType) {
public @Nullable ICodeAnnotation searchUp(int position, int limitPos, AnnType annType) {
for (ICodeAnnotation v : navMap.subMap(position, true, limitPos, true).values()) {
if (v.getAnnType() == annType) {
return v;
Expand Down Expand Up @@ -99,28 +97,40 @@ public ICodeAnnotation getAt(int position) {

@Override
public ICodeNodeRef getNodeAt(int position) {
return navMap.tailMap(position, true)
.values().stream()
.flatMap(CodeMetadataStorage::mapEnclosingNode)
.findFirst().orElse(null);
int nesting = 0;
for (ICodeAnnotation ann : navMap.tailMap(position, true).values()) {
switch (ann.getAnnType()) {
case END:
nesting++;
break;

case DECLARATION:
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
AnnType nodeType = node.getAnnType();
if (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {
if (nesting == 0) {
return node;
}
nesting--;
}
break;
}
}
return null;
}

@Override
public ICodeNodeRef getNodeBelow(int position) {
return navMap.headMap(position, true).descendingMap()
.values().stream()
.flatMap(CodeMetadataStorage::mapEnclosingNode)
.findFirst().orElse(null);
}

private static Stream<ICodeNodeRef> mapEnclosingNode(ICodeAnnotation ann) {
if (ann instanceof NodeDeclareRef) {
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
if (node instanceof ClassNode || node instanceof MethodNode) {
return Stream.of(node);
for (ICodeAnnotation ann : navMap.headMap(position, true).descendingMap().values()) {
if (ann.getAnnType() == AnnType.DECLARATION) {
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
AnnType nodeType = node.getAnnType();
if (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {
return node;
}
}
}
return Stream.empty();
return null;
}

@Override
Expand All @@ -135,7 +145,7 @@ public Map<Integer, Integer> getLineMapping() {

@Override
public String toString() {
return "CodeMetadata{lines=" + lines
+ ", annotations=\n" + Utils.listToString(navMap.entrySet(), "\n") + "\n}";
return "CodeMetadata{\nlines=" + lines
+ "\nannotations=\n " + Utils.listToString(navMap.descendingMap().entrySet(), "\n ") + "\n}";
}
}
3 changes: 3 additions & 0 deletions jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import jadx.api.ICodeInfo;
import jadx.api.ICodeWriter;
import jadx.api.JadxArgs;
import jadx.api.metadata.annotations.NodeEnd;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.api.plugins.input.data.annotations.EncodedType;
import jadx.api.plugins.input.data.annotations.EncodedValue;
Expand Down Expand Up @@ -256,6 +257,7 @@ public void addClassBody(ICodeWriter clsCode, boolean printClassName) throws Cod
addInnerClsAndMethods(clsCode);
clsCode.decIndent();
clsCode.startLine('}');
clsCode.attachAnnotation(NodeEnd.VALUE);
}

private void addInnerClsAndMethods(ICodeWriter clsCode) {
Expand Down Expand Up @@ -369,6 +371,7 @@ public void addMethodCode(ICodeWriter code, MethodNode mth) throws CodegenExcept
mthGen.addInstructions(code);
code.decIndent();
code.startLine('}');
code.attachAnnotation(NodeEnd.VALUE);
}
}

Expand Down
1 change: 1 addition & 0 deletions jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ private void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, Constru
ctor.add(AFlag.DONT_GENERATE);
}
}
code.attachDefinition(cls);
code.add("new ");
useClass(code, parent);
MethodNode callMth = mth.root().resolveMethod(insn.getCallMth());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package jadx.tests.integration.others;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.Test;

import jadx.api.JadxInternalAccess;
import jadx.api.JavaClass;
import jadx.api.JavaMethod;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeAnnotation.AnnType;
import jadx.api.metadata.ICodeMetadata;
import jadx.api.metadata.ICodeNodeRef;
import jadx.core.dex.nodes.ClassNode;
Expand Down Expand Up @@ -53,14 +55,25 @@ public void test() {
int callUse = callUsePlaces.get(0);

ICodeMetadata metadata = cls.getCode().getCodeMetadata();
System.out.println(metadata);
ICodeNodeRef callDef = metadata.getNodeAt(callUse);
assertThat(callDef).isSameAs(testMth);

int beforeCallDef = callDefPos - 10;
ICodeAnnotation closest = metadata.getClosestUp(beforeCallDef);
AtomicInteger endPos = new AtomicInteger();
ICodeAnnotation testEnd = metadata.searchUp(callDefPos, (pos, ann) -> {
if (ann.getAnnType() == AnnType.END) {
endPos.set(pos);
return ann;
}
return null;
});
assertThat(testEnd).isNotNull();
int testEndPos = endPos.get();

ICodeAnnotation closest = metadata.getClosestUp(testEndPos);
assertThat(closest).isInstanceOf(FieldNode.class); // field reference from 'return a.str;'

ICodeNodeRef nodeBelow = metadata.getNodeBelow(beforeCallDef);
ICodeNodeRef nodeBelow = metadata.getNodeBelow(testEndPos);
assertThat(nodeBelow).isSameAs(callMth);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package jadx.tests.integration.others;

import java.util.List;

import org.junit.jupiter.api.Test;

import jadx.api.JavaClass;
import jadx.api.JavaMethod;
import jadx.api.metadata.ICodeMetadata;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.tests.api.IntegrationTest;

import static jadx.api.JadxInternalAccess.convertClassNode;
import static jadx.api.JadxInternalAccess.convertMethodNode;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

public class TestCodeMetadata2 extends IntegrationTest {

public static class TestCls {
@SuppressWarnings("Convert2Lambda")
public Runnable test(boolean a) {
if (a) {
return new Runnable() {
@Override
public void run() {
System.out.println("test");
}
};
}
System.out.println("another");
return empty();
}

public static Runnable empty() {
return new Runnable() {
@Override
public void run() {
// empty
}
};
}
}

@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
assertThat(cls).code().containsOne("return empty();");

MethodNode testMth = getMethod(cls, "test");
MethodNode emptyMth = getMethod(cls, "empty");

JavaClass javaClass = convertClassNode(jadxDecompiler, cls);
JavaMethod emptyJavaMethod = convertMethodNode(jadxDecompiler, emptyMth);
List<Integer> emptyUsePlaces = javaClass.getUsePlacesFor(javaClass.getCodeInfo(), emptyJavaMethod);
assertThat(emptyUsePlaces).hasSize(1);
int callUse = emptyUsePlaces.get(0);

ICodeMetadata metadata = cls.getCode().getCodeMetadata();
assertThat(metadata.getNodeAt(callUse)).isSameAs(testMth);
}
}
2 changes: 1 addition & 1 deletion jadx-gui/src/main/java/jadx/gui/JadxWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public JavaNode getJavaNodeByRef(ICodeNodeRef nodeRef) {
return getDecompiler().getJavaNodeByRef(nodeRef);
}

public JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {
public @Nullable JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {
return getDecompiler().getEnclosingNode(codeInfo, pos);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import jadx.gui.search.ISearchMethod;
import jadx.gui.search.ISearchProvider;
import jadx.gui.search.SearchSettings;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JNodeCache;
Expand All @@ -33,7 +34,7 @@ protected JNode convert(JavaNode node) {
return nodeCache.makeFrom(node);
}

protected JNode convert(JavaClass cls) {
protected JClass convert(JavaClass cls) {
return nodeCache.makeFrom(cls);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
import jadx.gui.jobs.Cancelable;
import jadx.gui.search.SearchSettings;
import jadx.gui.treemodel.CodeNode;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;

import static jadx.core.utils.Utils.getOrElse;

public final class CodeSearchProvider extends BaseSearchProvider {
private static final Logger LOG = LoggerFactory.getLogger(CodeSearchProvider.class);

Expand Down Expand Up @@ -68,10 +71,12 @@ private JNode searchNext(JavaClass javaClass, String clsCode) {
int end = lineEnd == -1 ? clsCode.length() : lineEnd;
String line = clsCode.substring(lineStart, end);
this.pos = end;
return new CodeNode(getEnclosingNode(javaClass, end), line.trim(), newPos);
JClass rootCls = convert(javaClass);
JNode enclosingNode = getOrElse(getEnclosingNode(javaClass, end), rootCls);
return new CodeNode(rootCls, enclosingNode, line.trim(), newPos);
}

private JNode getEnclosingNode(JavaClass javaCls, int pos) {
private @Nullable JNode getEnclosingNode(JavaClass javaCls, int pos) {
try {
ICodeMetadata metadata = javaCls.getCodeInfo().getCodeMetadata();
ICodeNodeRef nodeRef = metadata.getNodeAt(pos);
Expand All @@ -82,7 +87,7 @@ private JNode getEnclosingNode(JavaClass javaCls, int pos) {
} catch (Exception e) {
LOG.debug("Failed to resolve enclosing node", e);
}
return convert(javaCls);
return null;
}

private String getClassCode(JavaClass javaClass, ICodeCache codeCache) {
Expand Down
13 changes: 4 additions & 9 deletions jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
public class CodeNode extends JNode {
private static final long serialVersionUID = 1658650786734966545L;

private final transient JClass rootCls;
private final transient JNode jNode;
private final transient String line;
private final transient int pos;

public CodeNode(JNode jNode, String lineStr, int pos) {
public CodeNode(JClass rootCls, JNode jNode, String lineStr, int pos) {
this.rootCls = rootCls;
this.jNode = jNode;
this.line = lineStr;
this.pos = pos;
Expand All @@ -36,14 +38,7 @@ public JClass getJParent() {

@Override
public JClass getRootClass() {
JClass parent = jNode.getJParent();
if (parent != null) {
return parent.getRootClass();
}
if (jNode instanceof JClass) {
return (JClass) jNode;
}
return null;
return rootCls;
}

@Override
Expand Down
8 changes: 6 additions & 2 deletions jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import jadx.gui.JadxWrapper;
import jadx.gui.jobs.TaskStatus;
import jadx.gui.treemodel.CodeNode;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JMethod;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JNodeCache;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;

Expand Down Expand Up @@ -104,9 +106,11 @@ private void processUsage(JavaNode searchNode, JavaClass topUseClass) {
if (line.startsWith("import ")) {
continue;
}
JNodeCache nodeCache = getNodeCache();
JavaNode enclosingNode = wrapper.getEnclosingNode(codeInfo, pos);
JavaNode usageNode = enclosingNode == null ? topUseClass : enclosingNode;
usageList.add(new CodeNode(getNodeCache().makeFrom(usageNode), line.trim(), pos));
JClass rootJCls = nodeCache.makeFrom(topUseClass);
JNode usageJNode = enclosingNode == null ? rootJCls : nodeCache.makeFrom(enclosingNode);
usageList.add(new CodeNode(rootJCls, usageJNode, line.trim(), pos));
}
}

Expand Down
Loading

0 comments on commit f4b3645

Please sign in to comment.