fix(gui): prevent old refs leak in shortcuts controller

This commit is contained in:
Skylot 2024-09-21 21:53:31 +01:00
parent e6fde48b69
commit 02b69d2d29
No known key found for this signature in database
GPG Key ID: 47A4975761262B6A
4 changed files with 61 additions and 30 deletions

View File

@ -576,6 +576,7 @@ public class MainWindow extends JFrame {
LogCollector.getInstance().reset();
wrapper.close();
tabsController.forceCloseAllTabs();
shortcutsController.reset();
UiUtils.resetClipboardOwner();
System.gc();
update();

View File

@ -13,8 +13,7 @@ import jadx.gui.utils.UiUtils;
import jadx.gui.utils.shortcut.Shortcut;
import jadx.gui.utils.ui.ActionHandler;
public class JadxGuiAction extends ActionHandler
implements IShortcutAction {
public class JadxGuiAction extends ActionHandler implements IShortcutAction {
private static final String COMMAND = "JadxGuiAction.Command.%s";
private final ActionModel actionModel;
@ -24,7 +23,6 @@ public class JadxGuiAction extends ActionHandler
private Shortcut shortcut;
public JadxGuiAction(ActionModel actionModel) {
super();
this.actionModel = actionModel;
this.id = actionModel.name();
@ -48,7 +46,6 @@ public class JadxGuiAction extends ActionHandler
}
public JadxGuiAction(String id) {
super();
this.actionModel = null;
this.id = id;
@ -138,4 +135,9 @@ public class JadxGuiAction extends ActionHandler
addedKeyStroke = keyStroke;
}
}
@Override
public String toString() {
return "JadxGuiAction{" + id + ", component: " + shortcutComponent + '}';
}
}

View File

@ -1,6 +1,7 @@
package jadx.gui.ui.menu;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
@ -10,11 +11,18 @@ import jadx.gui.utils.shortcut.Shortcut;
import jadx.gui.utils.shortcut.ShortcutsController;
public class JadxMenu extends JMenu {
// fake component to fill action shortcut component property
private static final JComponent JADX_MENU_COMPONENT = new JComponent() {
@Override
public String toString() {
return "JADX_MENU_COMPONENT";
}
};
private final ShortcutsController shortcutsController;
public JadxMenu(String name, ShortcutsController shortcutsController) {
super(name);
this.shortcutsController = shortcutsController;
}
@ -33,7 +41,12 @@ public class JadxMenu extends JMenu {
public void bindAction(Action action) {
if (action instanceof JadxGuiAction) {
shortcutsController.bind((JadxGuiAction) action);
JadxGuiAction guiAction = (JadxGuiAction) action;
JComponent shortcutComponent = guiAction.getShortcutComponent();
if (shortcutComponent == null) {
guiAction.setShortcutComponent(JADX_MENU_COMPONENT);
}
shortcutsController.bind(guiAction);
}
}

View File

@ -3,6 +3,8 @@ package jadx.gui.utils.shortcut;
import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -12,41 +14,42 @@ import javax.swing.JComponent;
import javax.swing.KeyStroke;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.data.ShortcutsWrapper;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.action.ActionCategory;
import jadx.gui.ui.action.ActionModel;
import jadx.gui.ui.action.IShortcutAction;
import jadx.gui.utils.UiUtils;
public class ShortcutsController {
private ShortcutsWrapper shortcuts;
private static final Logger LOG = LoggerFactory.getLogger(ShortcutsController.class);
private final JadxSettings settings;
private final Map<ActionModel, Set<IShortcutAction>> boundActions = new EnumMap<>(ActionModel.class);
private final Set<ActionModel> mouseActions = EnumSet.noneOf(ActionModel.class);
private final Map<ActionModel, Set<IShortcutAction>> boundActions = new HashMap<>();
private Set<ActionModel> mouseActions = null;
private ShortcutsWrapper shortcuts;
public ShortcutsController(JadxSettings settings) {
this.settings = settings;
}
public void loadSettings() {
this.shortcuts = settings.getShortcuts();
shortcuts = settings.getShortcuts();
indexMouseActions();
for (Map.Entry<ActionModel, Set<IShortcutAction>> actionsEntry : boundActions.entrySet()) {
ActionModel actionModel = actionsEntry.getKey();
Set<IShortcutAction> actions = actionsEntry.getValue();
Shortcut shortcut = get(actionModel);
boundActions.forEach((actionModel, actions) -> {
if (actions != null) {
Shortcut shortcut = get(actionModel);
for (IShortcutAction action : actions) {
action.setShortcut(shortcut);
}
}
}
});
}
@Nullable
@ -56,17 +59,20 @@ public class ShortcutsController {
public KeyStroke getKeyStroke(ActionModel actionModel) {
Shortcut shortcut = get(actionModel);
KeyStroke keyStroke = null;
if (shortcut != null && shortcut.isKeyboard()) {
keyStroke = shortcut.toKeyStroke();
return shortcut.toKeyStroke();
}
return keyStroke;
return null;
}
/*
/**
* Binds to an action and updates its shortcut every time loadSettings is called
*/
public void bind(IShortcutAction action) {
if (action.getShortcutComponent() == null) {
LOG.warn("No shortcut component in action: {}", action, new JadxRuntimeException());
return;
}
boundActions.computeIfAbsent(action.getActionModel(), k -> new HashSet<>());
boundActions.get(action.getActionModel()).add(action);
}
@ -93,7 +99,6 @@ public class ShortcutsController {
if (mw.isSettingsOpen()) {
return;
}
if (!(event instanceof MouseEvent)) {
return;
}
@ -101,7 +106,6 @@ public class ShortcutsController {
if (mouseEvent.getID() != MouseEvent.MOUSE_PRESSED) {
return;
}
int mouseButton = mouseEvent.getButton();
for (ActionModel actionModel : mouseActions) {
Shortcut shortcut = shortcuts.get(actionModel);
@ -121,22 +125,33 @@ public class ShortcutsController {
}
private void indexMouseActions() {
mouseActions = new HashSet<>();
mouseActions.clear();
for (ActionModel actionModel : ActionModel.values()) {
Shortcut shortcut = shortcuts.get(actionModel);
if (shortcut != null && shortcut.isMouse()) {
mouseActions.add(actionModel);
} else {
mouseActions.remove(actionModel);
}
}
}
public void unbindActionsForComponent(JComponent component) {
for (ActionModel actionModel : ActionModel.values()) {
Set<IShortcutAction> actions = boundActions.get(actionModel);
for (Set<IShortcutAction> actions : boundActions.values()) {
if (actions != null) {
actions.removeIf(action -> action != null && action.getShortcutComponent() == component);
actions.removeIf(action -> action == null
|| action.getShortcutComponent() == null
|| action.getShortcutComponent() == component);
}
}
}
/**
* Keep only actions bound to the main window.
* Other actions will be added on demand.
*/
public void reset() {
for (ActionModel actionModel : ActionModel.values()) {
if (actionModel.getCategory() != ActionCategory.MENU_TOOLBAR) {
boundActions.remove(actionModel);
}
}
}