feat(plugins): add method to open usage dialog

This commit is contained in:
Skylot
2025-01-20 19:05:06 +00:00
parent 45d320a596
commit 19f3cdf501
7 changed files with 67 additions and 46 deletions
@@ -83,6 +83,11 @@ public interface JadxGuiContext {
*/
boolean open(ICodeNodeRef ref);
/**
* Open usage dialog for a node
*/
void openUsageDialog(ICodeNodeRef ref);
/**
* Reload code in active tab
*/
@@ -22,18 +22,15 @@ import jadx.api.plugins.events.types.NodeRenamedByUser;
import jadx.api.plugins.gui.ISettingsGroup;
import jadx.api.plugins.gui.JadxGuiContext;
import jadx.api.plugins.gui.JadxGuiSettings;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.plugins.PluginContext;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.AbstractCodeArea;
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.ui.dialog.UsageDialog;
import jadx.gui.ui.panel.ContentPanel;
import jadx.gui.utils.IconsCache;
import jadx.gui.utils.JNodeCache;
import jadx.gui.utils.UiUtils;
public class GuiPluginContext implements JadxGuiContext {
@@ -183,24 +180,19 @@ public class GuiPluginContext implements JadxGuiContext {
@Override
public boolean open(ICodeNodeRef ref) {
JNodeCache cache = commonContext.getMainWindow().getWrapper().getCache().getNodeCache();
JNode node;
if (ref instanceof ClassNode) {
node = cache.makeFrom(((ClassNode) ref).getJavaNode());
} else if (ref instanceof MethodNode) {
node = cache.makeFrom(((MethodNode) ref).getJavaNode());
} else if (ref instanceof FieldNode) {
node = cache.makeFrom(((FieldNode) ref).getJavaNode());
} else {
// Package node - cannot jump to it
// TODO: Var node - might be possible
return false;
}
commonContext.getMainWindow().getTabsController().codeJump(node);
commonContext.getMainWindow().getTabsController().codeJump(getJNodeFromRef(ref));
return true;
}
@Override
public void openUsageDialog(ICodeNodeRef ref) {
UsageDialog.open(commonContext.getMainWindow(), getJNodeFromRef(ref));
}
private JNode getJNodeFromRef(ICodeNodeRef ref) {
return commonContext.getMainWindow().getCacheObject().getNodeCache().makeFrom(ref);
}
@Override
public void reloadActiveTab() {
UiUtils.uiRun(() -> {
@@ -254,9 +254,9 @@ public class MainWindow extends JFrame implements ExportProjectDialog.ExportProj
public MainWindow(JadxSettings settings) {
this.settings = settings;
this.cacheObject = new CacheObject();
this.project = new JadxProject(this);
this.wrapper = new JadxWrapper(this);
this.cacheObject = new CacheObject(wrapper);
this.liveReloadWorker = new LiveReloadWorker(this);
this.renameMappings = new RenameMappingsGui(this);
this.cacheManager = new CacheManager(settings);
@@ -1,7 +1,6 @@
package jadx.gui.ui.codearea;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.action.ActionModel;
import jadx.gui.ui.dialog.UsageDialog;
@@ -14,15 +13,6 @@ public final class FindUsageAction extends JNodeAction {
@Override
public void runAction(JNode node) {
MainWindow mw = getCodeArea().getMainWindow();
UsageDialog usageDialog = new UsageDialog(mw, node);
mw.addLoadListener(loaded -> {
if (!loaded) {
usageDialog.dispose();
return true;
}
return false;
});
usageDialog.setVisible(true);
UsageDialog.open(getCodeArea().getMainWindow(), node);
}
}
@@ -42,7 +42,19 @@ public class UsageDialog extends CommonSearchDialog {
private transient List<CodeNode> usageList;
public UsageDialog(MainWindow mainWindow, JNode node) {
public static void open(MainWindow mainWindow, JNode node) {
UsageDialog usageDialog = new UsageDialog(mainWindow, node);
mainWindow.addLoadListener(loaded -> {
if (!loaded) {
usageDialog.dispose();
return true;
}
return false;
});
usageDialog.setVisible(true);
}
private UsageDialog(MainWindow mainWindow, JNode node) {
super(mainWindow, NLS.str("usage_dialog.title"));
this.node = node;
@@ -8,10 +8,12 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable;
import jadx.api.JavaClass;
import jadx.gui.JadxWrapper;
import jadx.gui.ui.dialog.SearchDialog;
import jadx.gui.utils.pkgs.PackageHelper;
public class CacheObject {
private final JadxWrapper wrapper;
private String lastSearch;
private JNodeCache jNodeCache;
@@ -23,13 +25,14 @@ public class CacheObject {
private volatile boolean fullDecompilationFinished;
public CacheObject() {
public CacheObject(JadxWrapper wrapper) {
this.wrapper = wrapper;
reset();
}
public void reset() {
lastSearch = null;
jNodeCache = new JNodeCache();
jNodeCache = new JNodeCache(wrapper);
lastSearchOptions = new HashMap<>();
lastSearchPackage = null;
decompileBatches = null;
@@ -8,7 +8,9 @@ import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.api.JavaVariable;
import jadx.api.metadata.ICodeNodeRef;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JField;
import jadx.gui.treemodel.JMethod;
@@ -16,36 +18,48 @@ import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JVariable;
public class JNodeCache {
private final JadxWrapper wrapper;
private final Map<ICodeNodeRef, JNode> cache = new ConcurrentHashMap<>();
private final Map<JavaNode, JNode> cache = new ConcurrentHashMap<>();
public JNodeCache(JadxWrapper wrapper) {
this.wrapper = wrapper;
}
public JNode makeFrom(ICodeNodeRef nodeRef) {
if (nodeRef == null) {
return null;
}
// don't use 'computeIfAbsent' method here, it will cause 'Recursive update' exception
JNode jNode = cache.get(nodeRef);
if (jNode == null || jNode.getJavaNode().getCodeNodeRef() != nodeRef) {
jNode = convert(nodeRef);
cache.put(nodeRef, jNode);
}
return jNode;
}
public JNode makeFrom(JavaNode javaNode) {
if (javaNode == null) {
return null;
}
// don't use 'computeIfAbsent' method here, it will cause 'Recursive update' exception
JNode jNode = cache.get(javaNode);
if (jNode == null || jNode.getJavaNode() != javaNode) {
jNode = convert(javaNode);
cache.put(javaNode, jNode);
}
return jNode;
return makeFrom(javaNode.getCodeNodeRef());
}
public JClass makeFrom(JavaClass javaCls) {
if (javaCls == null) {
return null;
}
JClass jCls = (JClass) cache.get(javaCls);
ICodeNodeRef nodeRef = javaCls.getCodeNodeRef();
JClass jCls = (JClass) cache.get(nodeRef);
if (jCls == null || jCls.getCls() != javaCls) {
jCls = convert(javaCls);
cache.put(javaCls, jCls);
cache.put(nodeRef, jCls);
}
return jCls;
}
public void remove(JavaNode javaNode) {
cache.remove(javaNode);
cache.remove(javaNode.getCodeNodeRef());
}
public void removeWholeClass(JavaClass javaCls) {
@@ -64,12 +78,17 @@ public class JNodeCache {
return new JClass(cls, makeFrom(parentCls));
}
private JNode convert(ICodeNodeRef nodeRef) {
JavaNode javaNode = wrapper.getDecompiler().getJavaNodeByRef(nodeRef);
return convert(javaNode);
}
private JNode convert(JavaNode node) {
if (node == null) {
return null;
}
if (node instanceof JavaClass) {
return convert(((JavaClass) node));
return convert((JavaClass) node);
}
if (node instanceof JavaMethod) {
return new JMethod((JavaMethod) node, makeFrom(node.getDeclaringClass()));