fix: handle null bounds in WindowLocation to prevent NPE on dialog dispose (PR #2826)

* fix: handle null bounds in WindowLocation to prevent NPE on dialog dispose

The equals(), hashCode(), and toString() methods in WindowLocation
could throw NullPointerException when bounds is null. This happens
when a dialog window is disposed before its bounds are fully
initialized (e.g., on macOS when closing the search dialog).

Use Objects.equals()/Objects.hashCode() for null-safe comparisons
and add a null guard in toString().

Fixes #2571

* additional null checks and null annotations

---------

Co-authored-by: easonysliu <easonysliu@tencent.com>
Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
eason
2026-03-19 03:05:49 +08:00
committed by GitHub
parent 165ae24722
commit 15ea9a56b9
2 changed files with 34 additions and 25 deletions
@@ -220,38 +220,49 @@ public class JadxSettings {
recentProjects.remove(projectPath);
}
private static String makeWindowId(Window window) {
return window.getClass().getSimpleName();
}
@SuppressWarnings("ConstantValue")
public void saveWindowPos(Window window) {
if (window == null) {
return;
}
synchronized (dataWriteSync) {
WindowLocation pos = new WindowLocation(window.getClass().getSimpleName(), window.getBounds());
settingsData.getWindowPos().put(pos.getWindowId(), pos);
Rectangle bounds = window.getBounds();
if (bounds != null) {
WindowLocation pos = new WindowLocation(makeWindowId(window), bounds);
settingsData.getWindowPos().put(pos.getWindowId(), pos);
}
}
}
public boolean loadWindowPos(Window window) {
Map<String, WindowLocation> windowPos = settingsData.getWindowPos();
WindowLocation pos = windowPos.get(window.getClass().getSimpleName());
if (pos == null || pos.getBounds() == null) {
String windowId = makeWindowId(window);
WindowLocation pos = settingsData.getWindowPos().get(windowId);
if (pos == null) {
return false;
}
if (!isAccessibleInAnyScreen(pos)) {
Rectangle bounds = pos.getBounds();
if (bounds == null || !isAccessibleInAnyScreen(windowId, bounds)) {
return false;
}
window.setBounds(pos.getBounds());
window.setBounds(bounds);
if (window instanceof MainWindow) {
((JFrame) window).setExtendedState(getMainWindowExtendedState());
}
return true;
}
private static boolean isAccessibleInAnyScreen(WindowLocation pos) {
Rectangle windowBounds = pos.getBounds();
private static boolean isAccessibleInAnyScreen(String windowId, Rectangle windowBounds) {
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
Rectangle screenBounds = gd.getDefaultConfiguration().getBounds();
if (screenBounds.intersects(windowBounds)) {
return true;
}
}
LOG.debug("Window saved position was ignored: {}", pos);
LOG.debug("Window saved position was ignored: {}, bounds: {}", windowId, windowBounds);
return false;
}
@@ -1,16 +1,20 @@
package jadx.gui.settings;
import java.awt.Rectangle;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("unused")
public class WindowLocation {
private String windowId;
private Rectangle bounds;
private @Nullable Rectangle bounds;
// Don't remove. Used in json serialization
// Don't remove. Used in JSON serialization
public WindowLocation() {
}
public WindowLocation(String windowId, Rectangle bounds) {
public WindowLocation(String windowId, @Nullable Rectangle bounds) {
this.windowId = windowId;
this.bounds = bounds;
}
@@ -23,37 +27,31 @@ public class WindowLocation {
this.windowId = windowId;
}
public Rectangle getBounds() {
public @Nullable Rectangle getBounds() {
return bounds;
}
public void setBounds(Rectangle bounds) {
public void setBounds(@Nullable Rectangle bounds) {
this.bounds = bounds;
}
@Override
public int hashCode() {
return windowId.hashCode();
return Objects.hashCode(windowId);
}
@Override
public final boolean equals(Object o) {
if (o instanceof WindowLocation) {
WindowLocation that = (WindowLocation) o;
return windowId.equals(that.windowId) && bounds.equals(that.bounds);
return Objects.equals(windowId, that.windowId)
&& Objects.equals(bounds, that.bounds);
}
return false;
}
@Override
public String toString() {
return "WindowLocation{"
+ "id='" + windowId + '\''
+ ", x=" + bounds.getX()
+ ", y=" + bounds.getY()
+ ", width=" + bounds.getWidth()
+ ", height=" + bounds.getHeight()
+ '}';
return "WindowLocation{id=" + windowId + ", bounds=" + bounds + '}';
}
}