Fix NPE in the Starlark debugger
If we breakpoint at a line before a local (that is a cell) has been intialized, cell.x is null, which we shouldn't attempt to add to the returned map of locals.
Fixes https://github.com/bazelbuild/bazel/issues/24339
PiperOrigin-RevId: 698316990
Change-Id: Ifb45650fb41471c47605292af9873e9f66b7f7d7
diff --git a/src/main/java/net/starlark/java/eval/StarlarkThread.java b/src/main/java/net/starlark/java/eval/StarlarkThread.java
index 18f85c3..b073a8b 100644
--- a/src/main/java/net/starlark/java/eval/StarlarkThread.java
+++ b/src/main/java/net/starlark/java/eval/StarlarkThread.java
@@ -187,10 +187,10 @@
if (fn instanceof StarlarkFunction) {
for (int i = 0; i < locals.length; i++) {
Object local = locals[i];
+ if (local instanceof StarlarkFunction.Cell) {
+ local = ((StarlarkFunction.Cell) local).x;
+ }
if (local != null) {
- if (local instanceof StarlarkFunction.Cell) {
- local = ((StarlarkFunction.Cell) local).x;
- }
env.put(((StarlarkFunction) fn).rfn.getLocals().get(i).getName(), local);
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java b/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
index 878f1b5..8648802 100644
--- a/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
@@ -413,6 +413,119 @@
}
@Test
+ public void testListFramesWithUninitializedCellRequest() throws Exception {
+ sendStartDebuggingRequest();
+ ParserInput bzlFile =
+ createInput(
+ "/a/build/file/foo.bzl",
+ """
+ def outer():
+ x = [1,2,3] # <- breakpoint
+
+ def inner():
+ x.append(4)
+
+ inner()
+
+ outer()
+ """);
+
+ Location breakpoint =
+ Location.newBuilder().setLineNumber(2).setPath("/a/build/file/foo.bzl").build();
+ setBreakpoints(ImmutableList.of(breakpoint));
+
+ Module module = Module.create();
+ Thread evaluationThread = execInWorkerThread(bzlFile, module);
+ long threadId = evaluationThread.getId();
+
+ // wait for breakpoint to be hit
+ client.waitForEvent(DebugEvent::hasThreadPaused, Duration.ofSeconds(5));
+
+ ListFramesResponse frames = listFrames(threadId);
+ assertThat(frames.getFrameCount()).isEqualTo(2);
+ // critically x is not present as a local
+ assertFramesEqualIgnoringValueIdentifiers(
+ frames.getFrame(0),
+ Frame.newBuilder()
+ .setFunctionName("outer")
+ .setLocation(breakpoint.toBuilder().setColumnNumber(4))
+ .addScope(
+ Scope.newBuilder()
+ .setName("global")
+ .addBinding(getValueProto("outer", module.getGlobal("outer"))))
+ .build());
+ assertFramesEqualIgnoringValueIdentifiers(
+ frames.getFrame(1),
+ Frame.newBuilder()
+ .setFunctionName(StarlarkThread.TOP_LEVEL)
+ .setLocation(breakpoint.toBuilder().setLineNumber(9).setColumnNumber(6))
+ .addScope(
+ Scope.newBuilder()
+ .setName("global")
+ .addBinding(getValueProto("outer", module.getGlobal("outer"))))
+ .build());
+ }
+
+ @Test
+ public void testListFramesWithInitializedCellRequest() throws Exception {
+ sendStartDebuggingRequest();
+ ParserInput bzlFile =
+ createInput(
+ "/a/build/file/foo.bzl",
+ """
+ def outer():
+ x = [1]
+ pass # <- breakpoint
+
+ def inner():
+ x.append(4)
+
+ inner()
+
+ outer()
+ """);
+
+ Location breakpoint =
+ Location.newBuilder().setLineNumber(3).setPath("/a/build/file/foo.bzl").build();
+ setBreakpoints(ImmutableList.of(breakpoint));
+
+ Module module = Module.create();
+ Thread evaluationThread = execInWorkerThread(bzlFile, module);
+ long threadId = evaluationThread.getId();
+
+ // wait for breakpoint to be hit
+ client.waitForEvent(DebugEvent::hasThreadPaused, Duration.ofSeconds(5));
+
+ ListFramesResponse frames = listFrames(threadId);
+ assertThat(frames.getFrameCount()).isEqualTo(2);
+ // critically x is present as a local
+ assertFramesEqualIgnoringValueIdentifiers(
+ frames.getFrame(0),
+ Frame.newBuilder()
+ .setFunctionName("outer")
+ .setLocation(breakpoint.toBuilder().setColumnNumber(4))
+ .addScope(
+ Scope.newBuilder()
+ .setName("local")
+ .addBinding(getValueProto("x", StarlarkList.immutableOf(StarlarkInt.of(1)))))
+ .addScope(
+ Scope.newBuilder()
+ .setName("global")
+ .addBinding(getValueProto("outer", module.getGlobal("outer"))))
+ .build());
+ assertFramesEqualIgnoringValueIdentifiers(
+ frames.getFrame(1),
+ Frame.newBuilder()
+ .setFunctionName(StarlarkThread.TOP_LEVEL)
+ .setLocation(breakpoint.toBuilder().setLineNumber(10).setColumnNumber(6))
+ .addScope(
+ Scope.newBuilder()
+ .setName("global")
+ .addBinding(getValueProto("outer", module.getGlobal("outer"))))
+ .build());
+ }
+
+ @Test
public void testGetChildrenRequest() throws Exception {
sendStartDebuggingRequest();
ParserInput buildFile =