未验证 提交 f4b36454 编写于 作者: S Skylot

fix: ignore anonymous classes in enclosing node search (#1580)

上级 c27f2bad
......@@ -9,7 +9,8 @@ public interface ICodeAnnotation {
VAR,
VAR_REF,
DECLARATION,
OFFSET
OFFSET,
END // class or method body end
}
AnnType getAnnType();
......
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";
}
}
......@@ -6,16 +6,14 @@ import java.util.Map;
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 {
......@@ -55,7 +53,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
}
@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;
......@@ -65,7 +63,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
}
@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;
......@@ -99,28 +97,40 @@ public class CodeMetadataStorage implements ICodeMetadata {
@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
......@@ -135,7 +145,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
@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}";
}
}
......@@ -19,6 +19,7 @@ import jadx.api.CommentsLevel;
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;
......@@ -256,6 +257,7 @@ public class ClassGen {
addInnerClsAndMethods(clsCode);
clsCode.decIndent();
clsCode.startLine('}');
clsCode.attachAnnotation(NodeEnd.VALUE);
}
private void addInnerClsAndMethods(ICodeWriter clsCode) {
......@@ -369,6 +371,7 @@ public class ClassGen {
mthGen.addInstructions(code);
code.decIndent();
code.startLine('}');
code.attachAnnotation(NodeEnd.VALUE);
}
}
......
......@@ -761,6 +761,7 @@ public class InsnGen {
ctor.add(AFlag.DONT_GENERATE);
}
}
code.attachDefinition(cls);
code.add("new ");
useClass(code, parent);
MethodNode callMth = mth.root().resolveMethod(insn.getCallMth());
......
package jadx.tests.integration.others;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
......@@ -8,6 +9,7 @@ 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;
......@@ -53,14 +55,25 @@ public class TestCodeMetadata extends IntegrationTest {
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);
}
}
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);
}
}
......@@ -228,7 +228,7 @@ public class JadxWrapper {
return getDecompiler().getJavaNodeByRef(nodeRef);
}
public JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {
public @Nullable JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {
return getDecompiler().getEnclosingNode(codeInfo, pos);
}
......
......@@ -7,6 +7,7 @@ import jadx.api.JavaNode;
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;
......@@ -33,7 +34,7 @@ public abstract class BaseSearchProvider implements ISearchProvider {
return nodeCache.makeFrom(node);
}
protected JNode convert(JavaClass cls) {
protected JClass convert(JavaClass cls) {
return nodeCache.makeFrom(cls);
}
......
......@@ -16,9 +16,12 @@ import jadx.gui.JadxWrapper;
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);
......@@ -68,10 +71,12 @@ public final class CodeSearchProvider extends BaseSearchProvider {
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);
......@@ -82,7 +87,7 @@ public final class CodeSearchProvider extends BaseSearchProvider {
} catch (Exception e) {
LOG.debug("Failed to resolve enclosing node", e);
}
return convert(javaCls);
return null;
}
private String getClassCode(JavaClass javaClass, ICodeCache codeCache) {
......
......@@ -9,11 +9,13 @@ import jadx.api.JavaNode;
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;
......@@ -36,14 +38,7 @@ public class CodeNode extends JNode {
@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
......
......@@ -23,9 +23,11 @@ import jadx.api.utils.CodeUtils;
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;
......@@ -104,9 +106,11 @@ public class UsageDialog extends CommonSearchDialog {
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));
}
}
......
......@@ -42,7 +42,7 @@ import static java.nio.file.StandardOpenOption.WRITE;
public class DiskCodeCache implements ICodeCache {
private static final Logger LOG = LoggerFactory.getLogger(DiskCodeCache.class);
private static final int DATA_FORMAT_VERSION = 11;
private static final int DATA_FORMAT_VERSION = 12;
private static final byte[] JADX_NAMES_MAP_HEADER = "jadxnm".getBytes(StandardCharsets.US_ASCII);
......
......@@ -38,6 +38,7 @@ public class CodeAnnotationAdapter implements DataAdapter<ICodeAnnotation> {
map.put(AnnType.VAR, new VarNodeAdapter(mthAdapter));
map.put(AnnType.VAR_REF, VarRefAdapter.INSTANCE);
map.put(AnnType.OFFSET, InsnCodeOffsetAdapter.INSTANCE);
map.put(AnnType.END, new NodeEndAdapter());
return map;
}
......
package jadx.gui.utils.codecache.disk.adapters;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import jadx.api.metadata.annotations.NodeEnd;
public class NodeEndAdapter implements DataAdapter<NodeEnd> {
@Override
public void write(DataOutput out, NodeEnd value) throws IOException {
}
@Override
public NodeEnd read(DataInput in) throws IOException {
return NodeEnd.VALUE;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册