feat(script): add methods for apply renames and reload tabs (#2008)
This commit is contained in:
@@ -11,6 +11,11 @@ public class NodeRenamedByUser implements IJadxEvent {
|
||||
private final String oldName;
|
||||
private final String newName;
|
||||
|
||||
/**
|
||||
* Optional JRenameNode instance
|
||||
*/
|
||||
private Object renameNode;
|
||||
|
||||
public NodeRenamedByUser(ICodeNodeRef node, String oldName, String newName) {
|
||||
this.node = node;
|
||||
this.oldName = oldName;
|
||||
@@ -29,6 +34,14 @@ public class NodeRenamedByUser implements IJadxEvent {
|
||||
return newName;
|
||||
}
|
||||
|
||||
public Object getRenameNode() {
|
||||
return renameNode;
|
||||
}
|
||||
|
||||
public void setRenameNode(Object renameNode) {
|
||||
this.renameNode = renameNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxEventType<NodeRenamedByUser> getType() {
|
||||
return JadxEvents.NODE_RENAMED_BY_USER;
|
||||
|
||||
@@ -64,4 +64,19 @@ public interface JadxGuiContext {
|
||||
* @return if successfully jumped to the code ref
|
||||
*/
|
||||
boolean open(ICodeNodeRef ref);
|
||||
|
||||
/**
|
||||
* Reload code in active tab
|
||||
*/
|
||||
void reloadActiveTab();
|
||||
|
||||
/**
|
||||
* Reload code in all open tabs
|
||||
*/
|
||||
void reloadAllTabs();
|
||||
|
||||
/**
|
||||
* Save node rename in a project and run all needed UI updates
|
||||
*/
|
||||
void applyNodeRename(ICodeNodeRef node);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package jadx.api.plugins.pass.impl;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.plugins.pass.JadxPassInfo;
|
||||
import jadx.api.plugins.pass.types.JadxAfterLoadPass;
|
||||
|
||||
public class SimpleAfterLoadPass implements JadxAfterLoadPass {
|
||||
|
||||
private final JadxPassInfo info;
|
||||
private final Consumer<JadxDecompiler> init;
|
||||
|
||||
public SimpleAfterLoadPass(String name, Consumer<JadxDecompiler> init) {
|
||||
this.info = new SimpleJadxPassInfo(name);
|
||||
this.init = init;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JadxPassInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JadxDecompiler decompiler) {
|
||||
init.accept(decompiler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package jadx.gui.events.services;
|
||||
|
||||
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 java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.data.ICodeRename;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.plugins.events.JadxEvents;
|
||||
import jadx.api.plugins.events.types.NodeRenamedByUser;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.jobs.TaskStatus;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JRenameNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
/**
|
||||
* Rename service listen for user rename events.
|
||||
* For each event:
|
||||
* - add/update rename entry in project code data
|
||||
* - update code and/or invalidate cache for related classes
|
||||
* - apply all needed UI updates (tabs, classes tree)
|
||||
*/
|
||||
public class RenameService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RenameService.class);
|
||||
|
||||
public static void init(MainWindow mainWindow) {
|
||||
RenameService renameService = new RenameService(mainWindow);
|
||||
mainWindow.events().addListener(JadxEvents.NODE_RENAMED_BY_USER, renameService::process);
|
||||
}
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
|
||||
private RenameService(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
private void process(NodeRenamedByUser event) {
|
||||
try {
|
||||
LOG.debug("Applying rename event: {}", event);
|
||||
JRenameNode node = getRenameNode(event);
|
||||
updateCodeRenames(set -> processRename(node, event.getNewName(), set));
|
||||
refreshState(node);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Rename failed", e);
|
||||
UiUtils.errorMessage(mainWindow, "Rename failed:\n" + Utils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull JRenameNode getRenameNode(NodeRenamedByUser event) {
|
||||
Object renameNode = event.getRenameNode();
|
||||
if (renameNode instanceof JRenameNode) {
|
||||
return (JRenameNode) renameNode;
|
||||
}
|
||||
JadxDecompiler decompiler = mainWindow.getWrapper().getDecompiler();
|
||||
JavaNode javaNode = decompiler.getJavaNodeByRef(event.getNode());
|
||||
if (javaNode != null) {
|
||||
JNode node = mainWindow.getCacheObject().getNodeCache().makeFrom(javaNode);
|
||||
if (node instanceof JRenameNode) {
|
||||
return (JRenameNode) node;
|
||||
}
|
||||
}
|
||||
throw new JadxRuntimeException("Failed to resolve node: " + event.getNode());
|
||||
}
|
||||
|
||||
private void processRename(JRenameNode node, String newName, Set<ICodeRename> renames) {
|
||||
ICodeRename rename = node.buildCodeRename(newName, renames);
|
||||
renames.remove(rename);
|
||||
if (newName.isEmpty()) {
|
||||
node.removeAlias();
|
||||
} else {
|
||||
renames.add(rename);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCodeRenames(Consumer<Set<ICodeRename>> updater) {
|
||||
JadxProject project = mainWindow.getProject();
|
||||
JadxCodeData codeData = project.getCodeData();
|
||||
if (codeData == null) {
|
||||
codeData = new JadxCodeData();
|
||||
}
|
||||
Set<ICodeRename> set = new HashSet<>(codeData.getRenames());
|
||||
updater.accept(set);
|
||||
List<ICodeRename> list = new ArrayList<>(set);
|
||||
Collections.sort(list);
|
||||
codeData.setRenames(list);
|
||||
project.setCodeData(codeData);
|
||||
}
|
||||
|
||||
private void refreshState(JRenameNode node) {
|
||||
List<JavaNode> toUpdate = new ArrayList<>();
|
||||
node.addUpdateNodes(toUpdate);
|
||||
|
||||
JNodeCache nodeCache = mainWindow.getCacheObject().getNodeCache();
|
||||
Set<JClass> updatedTopClasses = toUpdate
|
||||
.stream()
|
||||
.map(JavaNode::getTopParentClass)
|
||||
.map(nodeCache::makeFrom)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
LOG.debug("Classes to update: {}", updatedTopClasses);
|
||||
if (updatedTopClasses.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mainWindow.getBackgroundExecutor().execute("Refreshing",
|
||||
() -> {
|
||||
mainWindow.getWrapper().reloadCodeData();
|
||||
UiUtils.uiRunAndWait(() -> refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses));
|
||||
refreshClasses(updatedTopClasses);
|
||||
},
|
||||
(status) -> {
|
||||
if (status == TaskStatus.CANCEL_BY_MEMORY) {
|
||||
mainWindow.showHeapUsageBar();
|
||||
UiUtils.errorMessage(mainWindow, NLS.str("message.memoryLow"));
|
||||
}
|
||||
node.reload(mainWindow);
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshClasses(Set<JClass> updatedTopClasses) {
|
||||
CacheObject cache = mainWindow.getCacheObject();
|
||||
if (updatedTopClasses.size() < 10) {
|
||||
// small batch => reload
|
||||
LOG.debug("Classes to reload: {}", updatedTopClasses.size());
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.reload(cache);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to reload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// big batch => unload
|
||||
LOG.debug("Classes to unload: {}", updatedTopClasses.size());
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.unload(cache);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to unload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshTabs(TabbedPane tabbedPane, Set<JClass> updatedClasses) {
|
||||
for (ContentPanel tab : tabbedPane.getTabs()) {
|
||||
JClass rootClass = tab.getNode().getRootClass();
|
||||
if (updatedClasses.remove(rootClass)) {
|
||||
ClassCodeContentPanel contentPanel = (ClassCodeContentPanel) tab;
|
||||
CodeArea codeArea = (CodeArea) contentPanel.getJavaCodePanel().getCodeArea();
|
||||
codeArea.refreshClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.plugins.events.IJadxEvents;
|
||||
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;
|
||||
@@ -19,10 +24,12 @@ 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.panel.ContentPanel;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
@@ -176,4 +183,45 @@ public class GuiPluginContext implements JadxGuiContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadActiveTab() {
|
||||
UiUtils.uiRun(() -> {
|
||||
CodeArea codeArea = getCodeArea();
|
||||
if (codeArea != null) {
|
||||
codeArea.refreshClass();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadAllTabs() {
|
||||
UiUtils.uiRun(() -> {
|
||||
for (ContentPanel contentPane : commonContext.getMainWindow().getTabbedPane().getTabs()) {
|
||||
if (contentPane instanceof AbstractCodeContentPanel) {
|
||||
AbstractCodeArea codeArea = ((AbstractCodeContentPanel) contentPane).getCodeArea();
|
||||
if (codeArea instanceof CodeArea) {
|
||||
((CodeArea) codeArea).refreshClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyNodeRename(ICodeNodeRef nodeRef) {
|
||||
JadxDecompiler decompiler = commonContext.getMainWindow().getWrapper().getDecompiler();
|
||||
JavaNode javaNode = decompiler.getJavaNodeByRef(nodeRef);
|
||||
if (javaNode == null) {
|
||||
throw new JadxRuntimeException("Failed to resolve node ref: " + nodeRef);
|
||||
}
|
||||
String newName;
|
||||
if (javaNode instanceof JavaClass) {
|
||||
// package can have alias
|
||||
newName = javaNode.getFullName();
|
||||
} else {
|
||||
newName = javaNode.getName();
|
||||
}
|
||||
IJadxEvents events = commonContext.getMainWindow().events();
|
||||
events.send(new NodeRenamedByUser(nodeRef, "", newName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +144,7 @@ public class ScriptContentPanel extends AbstractCodeContentPanel {
|
||||
scriptLog.error("Passes reload failed", e);
|
||||
}
|
||||
}, taskStatus -> {
|
||||
tabbedPane.reloadInactiveTabs();
|
||||
mainWindow.reloadTree();
|
||||
mainWindow.passesReloaded();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.cache.manager.CacheManager;
|
||||
import jadx.gui.device.debugger.BreakpointManager;
|
||||
import jadx.gui.events.services.RenameService;
|
||||
import jadx.gui.jobs.BackgroundExecutor;
|
||||
import jadx.gui.jobs.DecompileTask;
|
||||
import jadx.gui.jobs.ExportTask;
|
||||
@@ -580,7 +581,7 @@ public class MainWindow extends JFrame {
|
||||
initTree();
|
||||
updateLiveReload(project.isEnableLiveReload());
|
||||
BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());
|
||||
events().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));
|
||||
initEvents();
|
||||
|
||||
List<EditorViewState> openTabs = project.getOpenTabs(this);
|
||||
backgroundExecutor.execute(NLS.str("progress.load"),
|
||||
@@ -593,6 +594,17 @@ public class MainWindow extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
public void passesReloaded() {
|
||||
initEvents(); // TODO: events reset on reload passes on script run
|
||||
tabbedPane.reloadInactiveTabs();
|
||||
reloadTree();
|
||||
}
|
||||
|
||||
private void initEvents() {
|
||||
events().addListener(JadxEvents.RELOAD_PROJECT, ev -> UiUtils.uiRun(this::reopen));
|
||||
RenameService.init(this);
|
||||
}
|
||||
|
||||
public void updateLiveReload(boolean state) {
|
||||
if (liveReloadWorker.isStarted() == state) {
|
||||
return;
|
||||
|
||||
@@ -429,8 +429,16 @@ public class TabbedPane extends JTabbedPane {
|
||||
if (i == current) {
|
||||
continue;
|
||||
}
|
||||
JNode node = ((ContentPanel) getComponentAt(i)).getNode();
|
||||
ContentPanel oldPanel = (ContentPanel) getComponentAt(i);
|
||||
EditorViewState viewState = null;
|
||||
if (oldPanel instanceof IViewStateSupport) {
|
||||
viewState = ((IViewStateSupport) oldPanel).getEditorViewState();
|
||||
}
|
||||
JNode node = oldPanel.getNode();
|
||||
ContentPanel panel = node.getContentPanel(this);
|
||||
if (viewState != null && panel instanceof IViewStateSupport) {
|
||||
((IViewStateSupport) panel).restoreEditorViewState(viewState);
|
||||
}
|
||||
FocusManager.listen(panel);
|
||||
tabsMap.put(node, panel);
|
||||
setComponentAt(i, panel);
|
||||
|
||||
@@ -25,6 +25,6 @@ public final class RenameAction extends JNodeAction {
|
||||
|
||||
@Override
|
||||
public void runAction(JNode node) {
|
||||
RenameDialog.rename(getCodeArea().getMainWindow(), getCodeArea().getNode(), (JRenameNode) node);
|
||||
RenameDialog.rename(getCodeArea().getMainWindow(), (JRenameNode) node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,6 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
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 java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
@@ -26,29 +18,13 @@ import javax.swing.JTextField;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.data.ICodeRename;
|
||||
import jadx.api.data.impl.JadxCodeData;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.plugins.events.types.NodeRenamedByUser;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.gui.jobs.TaskStatus;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JPackage;
|
||||
import jadx.gui.treemodel.JRenameNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.TextStandardActions;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
@@ -59,17 +35,13 @@ import jadx.gui.utils.ui.NodeLabel;
|
||||
public class RenameDialog extends JDialog {
|
||||
private static final long serialVersionUID = -3269715644416902410L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RenameDialog.class);
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private final transient CacheObject cache;
|
||||
private final transient @Nullable JNode source;
|
||||
private final transient JRenameNode node;
|
||||
private transient JTextField renameField;
|
||||
private transient JButton renameBtn;
|
||||
|
||||
public static boolean rename(MainWindow mainWindow, @Nullable JNode source, JRenameNode node) {
|
||||
RenameDialog renameDialog = new RenameDialog(mainWindow, source, node);
|
||||
public static boolean rename(MainWindow mainWindow, JRenameNode node) {
|
||||
RenameDialog renameDialog = new RenameDialog(mainWindow, node);
|
||||
UiUtils.uiRun(() -> renameDialog.setVisible(true));
|
||||
UiUtils.uiRun(renameDialog::initRenameField); // wait for UI events to propagate
|
||||
return true;
|
||||
@@ -77,18 +49,16 @@ public class RenameDialog extends JDialog {
|
||||
|
||||
public static JPopupMenu buildRenamePopup(MainWindow mainWindow, JRenameNode node) {
|
||||
JMenuItem jmi = new JMenuItem(NLS.str("popup.rename"));
|
||||
jmi.addActionListener(action -> RenameDialog.rename(mainWindow, null, node));
|
||||
jmi.addActionListener(action -> RenameDialog.rename(mainWindow, node));
|
||||
jmi.setEnabled(node.canRename());
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
menu.add(jmi);
|
||||
return menu;
|
||||
}
|
||||
|
||||
private RenameDialog(MainWindow mainWindow, @Nullable JNode source, JRenameNode node) {
|
||||
private RenameDialog(MainWindow mainWindow, JRenameNode node) {
|
||||
super(mainWindow);
|
||||
this.mainWindow = mainWindow;
|
||||
this.cache = mainWindow.getCacheObject();
|
||||
this.source = source;
|
||||
this.node = node.replace();
|
||||
initUI();
|
||||
}
|
||||
@@ -112,125 +82,42 @@ public class RenameDialog extends JDialog {
|
||||
}
|
||||
|
||||
private void rename() {
|
||||
String newName = renameField.getText().trim();
|
||||
rename(renameField.getText().trim());
|
||||
}
|
||||
|
||||
private void resetName() {
|
||||
rename("");
|
||||
}
|
||||
|
||||
private void rename(String newName) {
|
||||
if (!checkNewName(newName)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
updateCodeRenames(set -> processRename(newName, set));
|
||||
refreshState();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Rename failed", e);
|
||||
UiUtils.errorMessage(this, "Rename failed:\n" + Utils.getStackTrace(e));
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void processRename(String newName, Set<ICodeRename> renames) {
|
||||
ICodeRename rename = node.buildCodeRename(newName, renames);
|
||||
renames.remove(rename);
|
||||
String oldName = node.getName();
|
||||
if (newName.isEmpty()) {
|
||||
node.removeAlias();
|
||||
sendRenameEvent(oldName, node.getJavaNode().getName());
|
||||
} else {
|
||||
renames.add(rename);
|
||||
sendRenameEvent(oldName, newName);
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
private void sendRenameEvent(String oldName, String newName) {
|
||||
ICodeNodeRef nodeRef = node.getJavaNode().getCodeNodeRef();
|
||||
mainWindow.events().send(new NodeRenamedByUser(nodeRef, oldName, newName));
|
||||
}
|
||||
|
||||
private void updateCodeRenames(Consumer<Set<ICodeRename>> updater) {
|
||||
JadxProject project = mainWindow.getProject();
|
||||
JadxCodeData codeData = project.getCodeData();
|
||||
if (codeData == null) {
|
||||
codeData = new JadxCodeData();
|
||||
}
|
||||
Set<ICodeRename> set = new HashSet<>(codeData.getRenames());
|
||||
updater.accept(set);
|
||||
List<ICodeRename> list = new ArrayList<>(set);
|
||||
Collections.sort(list);
|
||||
codeData.setRenames(list);
|
||||
project.setCodeData(codeData);
|
||||
}
|
||||
|
||||
private void refreshState() {
|
||||
List<JavaNode> toUpdate = new ArrayList<>();
|
||||
if (source != null && source != node) {
|
||||
toUpdate.add(source.getJavaNode());
|
||||
}
|
||||
node.addUpdateNodes(toUpdate);
|
||||
|
||||
JNodeCache nodeCache = cache.getNodeCache();
|
||||
Set<JClass> updatedTopClasses = toUpdate
|
||||
.stream()
|
||||
.map(JavaNode::getTopParentClass)
|
||||
.map(nodeCache::makeFrom)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
LOG.debug("Classes to update: {}", updatedTopClasses);
|
||||
if (updatedTopClasses.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mainWindow.getBackgroundExecutor().execute("Refreshing",
|
||||
() -> {
|
||||
mainWindow.getWrapper().reloadCodeData();
|
||||
UiUtils.uiRunAndWait(() -> refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses));
|
||||
refreshClasses(updatedTopClasses);
|
||||
},
|
||||
(status) -> {
|
||||
if (status == TaskStatus.CANCEL_BY_MEMORY) {
|
||||
mainWindow.showHeapUsageBar();
|
||||
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
|
||||
}
|
||||
node.reload(mainWindow);
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshClasses(Set<JClass> updatedTopClasses) {
|
||||
if (updatedTopClasses.size() < 10) {
|
||||
// small batch => reload
|
||||
LOG.debug("Classes to reload: {}", updatedTopClasses.size());
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.reload(cache);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to reload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// big batch => unload
|
||||
LOG.debug("Classes to unload: {}", updatedTopClasses.size());
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.unload(cache);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to unload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshTabs(TabbedPane tabbedPane, Set<JClass> updatedClasses) {
|
||||
for (ContentPanel tab : tabbedPane.getTabs()) {
|
||||
JClass rootClass = tab.getNode().getRootClass();
|
||||
if (updatedClasses.remove(rootClass)) {
|
||||
ClassCodeContentPanel contentPanel = (ClassCodeContentPanel) tab;
|
||||
CodeArea codeArea = (CodeArea) contentPanel.getJavaCodePanel().getCodeArea();
|
||||
codeArea.refreshClass();
|
||||
}
|
||||
}
|
||||
NodeRenamedByUser event = new NodeRenamedByUser(nodeRef, oldName, newName);
|
||||
event.setRenameNode(node);
|
||||
mainWindow.events().send(event);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JPanel initButtonsPanel() {
|
||||
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
|
||||
JButton resetButton = new JButton(NLS.str("common_dialog.reset"));
|
||||
resetButton.addActionListener(event -> resetName());
|
||||
|
||||
JButton cancelButton = new JButton(NLS.str("common_dialog.cancel"));
|
||||
cancelButton.addActionListener(event -> dispose());
|
||||
|
||||
renameBtn = new JButton(NLS.str("common_dialog.ok"));
|
||||
renameBtn.addActionListener(event -> rename());
|
||||
getRootPane().setDefaultButton(renameBtn);
|
||||
@@ -238,7 +125,8 @@ public class RenameDialog extends JDialog {
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
buttonPane.add(resetButton);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(renameBtn);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
|
||||
@@ -50,7 +50,7 @@ public class JPackagePopupMenu extends JPopupMenu {
|
||||
|
||||
private void rename(JRenamePackage pkg) {
|
||||
LOG.debug("Renaming package: {}", pkg);
|
||||
RenameDialog.rename(mainWindow, null, pkg);
|
||||
RenameDialog.rename(mainWindow, pkg);
|
||||
}
|
||||
|
||||
private JMenuItem makeExcludeItem(JPackage pkg) {
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=Abbrechen
|
||||
common_dialog.add=Hinzufügen
|
||||
common_dialog.update=Aktualisieren
|
||||
common_dialog.remove=Entfernen
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=Unterstützte Dateien
|
||||
file_dialog.load_dir_title=Verzeichnis laden
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=Cancel
|
||||
common_dialog.add=Add
|
||||
common_dialog.update=Update
|
||||
common_dialog.remove=Remove
|
||||
common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=Supported files
|
||||
file_dialog.load_dir_title=Load directory
|
||||
|
||||
@@ -104,6 +104,7 @@ nav.forward=Adelante
|
||||
#common_dialog.add=Add
|
||||
#common_dialog.update=Update
|
||||
#common_dialog.remove=Remove
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
#file_dialog.supported_files=Supported files
|
||||
#file_dialog.load_dir_title=Load directory
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=취소
|
||||
common_dialog.add=추가
|
||||
common_dialog.update=업데이트
|
||||
common_dialog.remove=삭제
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=지원되는 파일
|
||||
file_dialog.load_dir_title=디렉토리 불러오기
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=Cancelar
|
||||
common_dialog.add=Adicionar
|
||||
common_dialog.update=Atualizar
|
||||
common_dialog.remove=Remover
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=Arquivos suportados
|
||||
file_dialog.load_dir_title=Carregar diretório
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=Отмена
|
||||
common_dialog.add=Добавить
|
||||
common_dialog.update=Обновить
|
||||
common_dialog.remove=Убрать
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=Поддерж. файлы
|
||||
file_dialog.load_dir_title=Импортировать папку
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=取消
|
||||
common_dialog.add=添加
|
||||
common_dialog.update=更新
|
||||
common_dialog.remove=移除
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=支持的文件
|
||||
file_dialog.load_dir_title=加载目录
|
||||
|
||||
@@ -104,6 +104,7 @@ common_dialog.cancel=取消
|
||||
common_dialog.add=新增
|
||||
common_dialog.update=更新
|
||||
common_dialog.remove=移除
|
||||
#common_dialog.reset=Reset
|
||||
|
||||
file_dialog.supported_files=支援的檔案
|
||||
file_dialog.load_dir_title=載入目錄
|
||||
|
||||
@@ -18,7 +18,7 @@ jadx.addPass(object : ScriptDecompilePass(jadx, "RenameParams") {
|
||||
// parameter annotations stored in method attribute
|
||||
mth.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS)?.let { paramsAttr ->
|
||||
for ((paramNum, annAttr) in paramsAttr.paramList.withIndex()) {
|
||||
val name = annAttr.get(annCls)?.values?.get(annParam)?.value as String
|
||||
val name = annAttr?.get(annCls)?.values?.get(annParam)?.value as String?
|
||||
if (NameMapper.isValidIdentifier(name)) {
|
||||
mth.argRegs[paramNum].name = name
|
||||
log.info { "Rename param $paramNum to $name in method $mth" }
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ import jadx.plugins.script.runtime.data.Rename
|
||||
import jadx.plugins.script.runtime.data.Replace
|
||||
import jadx.plugins.script.runtime.data.Search
|
||||
import jadx.plugins.script.runtime.data.Stages
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.io.File
|
||||
|
||||
const val JADX_SCRIPT_LOG_PREFIX = "JadxScript:"
|
||||
@@ -73,5 +74,5 @@ class JadxScriptInstance(
|
||||
}
|
||||
|
||||
val internalDecompiler: JadxDecompiler
|
||||
get() = decompiler
|
||||
@Internal get() = decompiler
|
||||
}
|
||||
|
||||
+9
@@ -42,6 +42,10 @@ class Gui(
|
||||
|
||||
fun open(ref: ICodeNodeRef): Boolean = context().open(ref)
|
||||
|
||||
fun reloadActiveTab() = context().reloadActiveTab()
|
||||
|
||||
fun reloadAllTabs() = context().reloadAllTabs()
|
||||
|
||||
val nodeUnderCaret: ICodeNodeRef?
|
||||
get() = context().nodeUnderCaret
|
||||
val nodeUnderMouse: ICodeNodeRef?
|
||||
@@ -51,6 +55,11 @@ class Gui(
|
||||
val enclosingNodeUnderMouse: ICodeNodeRef?
|
||||
get() = context().enclosingNodeUnderMouse
|
||||
|
||||
/**
|
||||
* Save node rename in a project and run all needed UI updates
|
||||
*/
|
||||
fun applyNodeRename(node: ICodeNodeRef) = context().applyNodeRename(node)
|
||||
|
||||
private fun context(): JadxGuiContext =
|
||||
guiContext ?: throw IllegalStateException("GUI plugins context not available!")
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package jadx.plugins.script.runtime.data
|
||||
import jadx.core.dex.nodes.ClassNode
|
||||
import jadx.plugins.script.runtime.JadxScriptInstance
|
||||
|
||||
class Search(private val jadx: JadxScriptInstance) {
|
||||
class Search(jadx: JadxScriptInstance) {
|
||||
private val dec = jadx.internalDecompiler
|
||||
|
||||
fun classByFullName(fullName: String): ClassNode? {
|
||||
|
||||
Reference in New Issue
Block a user