Skip to content

Commit 98a7b7e

Browse files
trancexpressiloveeclipse
authored andcommitted
Bug 578899 - NPE with breakpoint in ClassLoader.getDefinedPackage()
This change adds null checks to callers of JDIStackFrame.getUnderlyingStackFrame(), to prevent NPEs that are not handled by JDT. Such NPEs result in an error dialog. JDIStackFrame.getUnderlyingStackFrame() can return null, if the JDIStackFrame object is used after JDIThread.disposeStackFrames() is called on the respective JDIThread. In particular, JDIStackFrame.fStackFrame is set to null on a thread resume. When getUnderlyingStackFrame() is called with a null in fStackFrame, and a suspended thread (e.g. the next breakpoint was hit), the method relies on JDIThread.computeStackFrames() to set the value of fStackFrame. This is done in JDIStackFrame.bind(). However, after a JDIThread.disposeStackFrames() call, JDIThread no longer has a reference to the original JDIStackFrame object, and so will not set its fStackFrame. So getUnderlyingStackFrame() returns a null. JDIThread.disposeStackFrames() is called when a thread start event is sent by the debuggee JVM, and JDT finds a JDIThread object to match the underlying JVM thread (the stack frames of that JDIThread are then cleared). Change-Id: I075de893c53f436c8c56e6834925c216cbf704fd Signed-off-by: Simeon Andreev <[email protected]> Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/191125 Tested-by: JDT Bot <[email protected]> Reviewed-by: Andrey Loskutov <[email protected]>
1 parent 96edf51 commit 98a7b7e

File tree

5 files changed

+38
-13
lines changed

5 files changed

+38
-13
lines changed

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugModelMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public class JDIDebugModelMessages extends NLS {
133133
public static String JDIStackFrame_ThrowingException;
134134
public static String JDIStackFrame_NoMethodReturnValue;
135135
public static String JDIStackFrame_NotObservedBecauseOfTimeout;
136+
public static String JDIStackFrame_NoLongerAvailable;
136137

137138
public static String JDIThisVariable_exception_while_retrieving_type_this;
138139
public static String JDIThisVariableexception_retrieving_reference_type_name;

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIDebugModelMessages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ JDIStackFrame_ExceptionThrown={0}() threw
100100
JDIStackFrame_ThrowingException={0}() is throwing
101101
JDIStackFrame_NoMethodReturnValue=no method return value
102102
JDIStackFrame_NotObservedBecauseOfTimeout=(Not observed to speed up the long running step operation)
103+
JDIStackFrame_NoLongerAvailable=Stack frame is no longer available
103104

104105
JDIThisVariable_exception_while_retrieving_type_this={0} occurred while retrieving type ''this''.
105106
JDIThisVariableexception_retrieving_reference_type_name={0} occurred retrieving reference type name.

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDILocalVariable.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.sun.jdi.InvalidTypeException;
2525
import com.sun.jdi.LocalVariable;
2626
import com.sun.jdi.ReferenceType;
27+
import com.sun.jdi.StackFrame;
2728
import com.sun.jdi.Type;
2829
import com.sun.jdi.Value;
2930

@@ -58,8 +59,10 @@ public JDILocalVariable(JDIStackFrame frame, LocalVariable local) {
5859
protected Value retrieveValue() throws DebugException {
5960
synchronized (fStackFrame.getThread()) {
6061
if (getStackFrame().isSuspended()) {
61-
return getStackFrame().getUnderlyingStackFrame().getValue(
62-
fLocal);
62+
StackFrame frame = getStackFrame().getUnderlyingStackFrame();
63+
if (frame != null) {
64+
return frame.getValue(fLocal);
65+
}
6366
}
6467
}
6568
// bug 6518
@@ -91,8 +94,16 @@ public String getName() throws DebugException {
9194
protected void setJDIValue(Value value) throws DebugException {
9295
try {
9396
synchronized (getStackFrame().getThread()) {
94-
getStackFrame().getUnderlyingStackFrame().setValue(getLocal(),
95-
value);
97+
StackFrame frame = getStackFrame().getUnderlyingStackFrame();
98+
if (frame != null) {
99+
frame.setValue(getLocal(), value);
100+
} else {
101+
String errorMessage = JDIDebugModelMessages.JDIStackFrame_NoLongerAvailable;
102+
targetRequestFailed(
103+
MessageFormat.format(
104+
JDIDebugModelMessages.JDILocalVariable_exception_modifying_local_variable_value,
105+
errorMessage), new Throwable(errorMessage)); // use Throwable, as RuntimeException is re-thrown
106+
}
96107
}
97108
fireChangeEvent(DebugEvent.CONTENT);
98109
} catch (ClassNotLoadedException e) {

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -760,13 +760,16 @@ protected void updateVariables() throws DebugException {
760760
}
761761
}
762762

763-
List<LocalVariable> locals = null;
763+
List<LocalVariable> locals = Collections.EMPTY_LIST;
764764
try {
765-
locals = getUnderlyingStackFrame().visibleVariables();
765+
StackFrame frame = getUnderlyingStackFrame();
766+
if (frame != null) {
767+
locals = frame.visibleVariables();
768+
}
766769
} catch (AbsentInformationException e) {
767-
locals = Collections.EMPTY_LIST;
770+
// continue with empty list of variables
768771
} catch (NativeMethodException e) {
769-
locals = Collections.EMPTY_LIST;
772+
// continue with empty list of variables
770773
} catch (RuntimeException e) {
771774
targetRequestFailed(
772775
MessageFormat.format(
@@ -980,7 +983,12 @@ protected List<LocalVariable> getUnderlyingVisibleVariables() throws DebugExcept
980983
synchronized (fThread) {
981984
List<LocalVariable> variables = Collections.EMPTY_LIST;
982985
try {
983-
variables = getUnderlyingStackFrame().visibleVariables();
986+
StackFrame frame = getUnderlyingStackFrame();
987+
if (frame != null) {
988+
variables = frame.visibleVariables();
989+
} else {
990+
setLocalsAvailable(false);
991+
}
984992
} catch (AbsentInformationException e) {
985993
setLocalsAvailable(false);
986994
} catch (NativeMethodException e) {
@@ -1354,7 +1362,7 @@ protected StackFrame getUnderlyingStackFrame() throws DebugException {
13541362
throw new DebugException(new Status(IStatus.ERROR,
13551363
JDIDebugPlugin.getUniqueIdentifier(),
13561364
IJavaStackFrame.ERR_INVALID_STACK_FRAME,
1357-
JDIDebugModelMessages.JDIStackFrame_25, null));
1365+
JDIDebugModelMessages.JDIStackFrame_25, new IllegalStateException()));
13581366
}
13591367
if (fThread.isSuspended()) {
13601368
// re-index stack frames - See Bug 47198
@@ -1364,14 +1372,14 @@ protected StackFrame getUnderlyingStackFrame() throws DebugException {
13641372
fThread.computeStackFrames();
13651373
if (fDepth == -1) {
13661374
// If depth is -1, then this is an invalid frame
1367-
throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), IJavaStackFrame.ERR_INVALID_STACK_FRAME, JDIDebugModelMessages.JDIStackFrame_25, null));
1375+
throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), IJavaStackFrame.ERR_INVALID_STACK_FRAME, JDIDebugModelMessages.JDIStackFrame_25, new IllegalStateException()));
13681376
}
13691377
}
13701378
} else {
13711379
throw new DebugException(new Status(IStatus.ERROR,
13721380
JDIDebugPlugin.getUniqueIdentifier(),
13731381
IJavaThread.ERR_THREAD_NOT_SUSPENDED,
1374-
JDIDebugModelMessages.JDIStackFrame_25, null));
1382+
JDIDebugModelMessages.JDIStackFrame_25, new IllegalStateException()));
13751383
}
13761384
}
13771385
return fStackFrame;

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIThread.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2361,7 +2361,11 @@ protected void step() throws DebugException {
23612361
return;
23622362
}
23632363
setOriginalStepKind(getStepKind());
2364-
Location location = top.getUnderlyingStackFrame().location();
2364+
StackFrame frame = top.getUnderlyingStackFrame();
2365+
if (frame == null) {
2366+
return;
2367+
}
2368+
Location location = frame.location();
23652369
setOriginalStepLocation(location);
23662370
setOriginalStepStackDepth(computeStackFrames().size());
23672371
setStepRequest(createStepRequest());

0 commit comments

Comments
 (0)