Skip to content

Commit 15ea9a5

Browse files
mango766easonysliuskylot
authored
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>
1 parent 165ae24 commit 15ea9a5

2 files changed

Lines changed: 34 additions & 25 deletions

File tree

jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,38 +220,49 @@ public void removeRecentProject(Path projectPath) {
220220
recentProjects.remove(projectPath);
221221
}
222222

223+
private static String makeWindowId(Window window) {
224+
return window.getClass().getSimpleName();
225+
}
226+
227+
@SuppressWarnings("ConstantValue")
223228
public void saveWindowPos(Window window) {
229+
if (window == null) {
230+
return;
231+
}
224232
synchronized (dataWriteSync) {
225-
WindowLocation pos = new WindowLocation(window.getClass().getSimpleName(), window.getBounds());
226-
settingsData.getWindowPos().put(pos.getWindowId(), pos);
233+
Rectangle bounds = window.getBounds();
234+
if (bounds != null) {
235+
WindowLocation pos = new WindowLocation(makeWindowId(window), bounds);
236+
settingsData.getWindowPos().put(pos.getWindowId(), pos);
237+
}
227238
}
228239
}
229240

230241
public boolean loadWindowPos(Window window) {
231-
Map<String, WindowLocation> windowPos = settingsData.getWindowPos();
232-
WindowLocation pos = windowPos.get(window.getClass().getSimpleName());
233-
if (pos == null || pos.getBounds() == null) {
242+
String windowId = makeWindowId(window);
243+
WindowLocation pos = settingsData.getWindowPos().get(windowId);
244+
if (pos == null) {
234245
return false;
235246
}
236-
if (!isAccessibleInAnyScreen(pos)) {
247+
Rectangle bounds = pos.getBounds();
248+
if (bounds == null || !isAccessibleInAnyScreen(windowId, bounds)) {
237249
return false;
238250
}
239-
window.setBounds(pos.getBounds());
251+
window.setBounds(bounds);
240252
if (window instanceof MainWindow) {
241253
((JFrame) window).setExtendedState(getMainWindowExtendedState());
242254
}
243255
return true;
244256
}
245257

246-
private static boolean isAccessibleInAnyScreen(WindowLocation pos) {
247-
Rectangle windowBounds = pos.getBounds();
258+
private static boolean isAccessibleInAnyScreen(String windowId, Rectangle windowBounds) {
248259
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
249260
Rectangle screenBounds = gd.getDefaultConfiguration().getBounds();
250261
if (screenBounds.intersects(windowBounds)) {
251262
return true;
252263
}
253264
}
254-
LOG.debug("Window saved position was ignored: {}", pos);
265+
LOG.debug("Window saved position was ignored: {}, bounds: {}", windowId, windowBounds);
255266
return false;
256267
}
257268

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package jadx.gui.settings;
22

33
import java.awt.Rectangle;
4+
import java.util.Objects;
45

6+
import org.jetbrains.annotations.Nullable;
7+
8+
@SuppressWarnings("unused")
59
public class WindowLocation {
610
private String windowId;
7-
private Rectangle bounds;
11+
private @Nullable Rectangle bounds;
812

9-
// Don't remove. Used in json serialization
13+
// Don't remove. Used in JSON serialization
1014
public WindowLocation() {
1115
}
1216

13-
public WindowLocation(String windowId, Rectangle bounds) {
17+
public WindowLocation(String windowId, @Nullable Rectangle bounds) {
1418
this.windowId = windowId;
1519
this.bounds = bounds;
1620
}
@@ -23,37 +27,31 @@ public void setWindowId(String windowId) {
2327
this.windowId = windowId;
2428
}
2529

26-
public Rectangle getBounds() {
30+
public @Nullable Rectangle getBounds() {
2731
return bounds;
2832
}
2933

30-
public void setBounds(Rectangle bounds) {
34+
public void setBounds(@Nullable Rectangle bounds) {
3135
this.bounds = bounds;
3236
}
3337

3438
@Override
3539
public int hashCode() {
36-
return windowId.hashCode();
40+
return Objects.hashCode(windowId);
3741
}
3842

3943
@Override
4044
public final boolean equals(Object o) {
4145
if (o instanceof WindowLocation) {
4246
WindowLocation that = (WindowLocation) o;
43-
return windowId.equals(that.windowId) && bounds.equals(that.bounds);
47+
return Objects.equals(windowId, that.windowId)
48+
&& Objects.equals(bounds, that.bounds);
4449
}
4550
return false;
46-
4751
}
4852

4953
@Override
5054
public String toString() {
51-
return "WindowLocation{"
52-
+ "id='" + windowId + '\''
53-
+ ", x=" + bounds.getX()
54-
+ ", y=" + bounds.getY()
55-
+ ", width=" + bounds.getWidth()
56-
+ ", height=" + bounds.getHeight()
57-
+ '}';
55+
return "WindowLocation{id=" + windowId + ", bounds=" + bounds + '}';
5856
}
5957
}

0 commit comments

Comments
 (0)