feat(script): add methods for apply renames and reload tabs (#2008)

This commit is contained in:
Skylot
2023-09-15 21:50:47 +01:00
parent b78a0257b4
commit c39a696977
23 changed files with 351 additions and 144 deletions
@@ -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" }
@@ -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
}
@@ -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!")
}
@@ -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? {