Skip to content

Commit c207739

Browse files
committed
[GR-41843] Prevent from NPE when client is not connected yet.
PullRequest: graal/12937
2 parents e61ab76 + 8b071af commit c207739

File tree

4 files changed

+79
-10
lines changed

4 files changed

+79
-10
lines changed

tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/DAPTestInstrument.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class DAPTestInstrument extends TruffleInstrument {
4747
protected void onCreate(final Env env) {
4848
env.registerService(new DAPSessionHandlerProvider() {
4949
@Override
50-
public DAPSessionHandler getSessionHandler(final boolean suspend, final boolean inspectInternal, final boolean inspectInitialization) throws IOException {
50+
public DAPSessionHandler getSessionHandler(final boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, Runnable prolog) throws IOException {
5151
return new DAPSessionHandler() {
5252

5353
private PipedOutputStream out;
@@ -59,7 +59,11 @@ DAPSessionHandler init() throws IOException {
5959
this.out = new PipedOutputStream();
6060
PipedOutputStream pos = new PipedOutputStream();
6161
in = new PipedInputStream(pos, 2048);
62-
DebugProtocolServer.Session.connect(DebugProtocolServerImpl.create(context, suspend, inspectInternal, inspectInitialization), new PipedInputStream(this.out, 2048), pos,
62+
DebugProtocolServerImpl dapServer = DebugProtocolServerImpl.create(context, suspend, inspectInternal, inspectInitialization);
63+
if (prolog != null) {
64+
prolog.run();
65+
}
66+
DebugProtocolServer.Session.connect(dapServer, new PipedInputStream(this.out, 2048), pos,
6367
Executors.newSingleThreadExecutor());
6468
return this;
6569
}
@@ -81,7 +85,7 @@ public InputStream getInputStream() {
8185
}
8286

8387
interface DAPSessionHandlerProvider {
84-
DAPSessionHandler getSessionHandler(boolean suspend, boolean inspectInternal, boolean inspectInitialization) throws IOException;
88+
DAPSessionHandler getSessionHandler(boolean suspend, boolean inspectInternal, boolean inspectInitialization, Runnable prolog) throws IOException;
8589
}
8690

8791
interface DAPSessionHandler {

tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/DAPTester.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.concurrent.Future;
4242
import java.util.concurrent.TimeUnit;
4343
import java.util.concurrent.TimeoutException;
44+
import java.util.function.Consumer;
4445
import java.util.stream.Collectors;
4546

4647
import org.graalvm.polyglot.Context;
@@ -62,18 +63,33 @@ public final class DAPTester {
6263
private final Context context;
6364
private Future<Value> lastValue;
6465

65-
private DAPTester(DAPSessionHandler handler, Engine engine) {
66+
private DAPTester(DAPSessionHandler handler, Context context) {
6667
this.handler = handler;
67-
this.context = Context.newBuilder().engine(engine).allowAllAccess(true).build();
68+
this.context = context;
6869
}
6970

7071
public static DAPTester start(boolean suspend) throws IOException {
72+
return start(suspend, null);
73+
}
74+
75+
public static DAPTester start(boolean suspend, Consumer<Context> prolog) throws IOException {
7176
final ProxyOutputStream err = new ProxyOutputStream(System.err);
7277
Engine engine = Engine.newBuilder().err(err).build();
78+
Context context = Context.newBuilder().engine(engine).allowAllAccess(true).build();
79+
Runnable runProlog;
80+
if (prolog != null) {
81+
runProlog = () -> prolog.accept(context);
82+
} else {
83+
runProlog = null;
84+
}
7385
Instrument testInstrument = engine.getInstruments().get(DAPTestInstrument.ID);
7486
DAPSessionHandlerProvider sessionHandlerProvider = testInstrument.lookup(DAPSessionHandlerProvider.class);
75-
DAPSessionHandler sessionHandler = sessionHandlerProvider.getSessionHandler(suspend, false, false);
76-
return new DAPTester(sessionHandler, engine);
87+
DAPSessionHandler sessionHandler = sessionHandlerProvider.getSessionHandler(suspend, false, false, runProlog);
88+
return new DAPTester(sessionHandler, context);
89+
}
90+
91+
Context getContext() {
92+
return context;
7793
}
7894

7995
public void finish() throws IOException {

tools/src/com.oracle.truffle.tools.dap.test/src/com/oracle/truffle/tools/dap/test/ITLDAPTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,52 @@ public void testOutput() throws Exception {
133133
tester.finish();
134134
}
135135

136+
@Test
137+
public void testOutputEarly() throws Exception {
138+
Source source1 = Source.newBuilder(InstrumentationTestLanguage.ID, "ROOT(\n" +
139+
" PRINT(OUT, \"Prologue to stdout\n\"),\n" +
140+
" PRINT(ERR, \"Prologue to stderr\n\")" +
141+
")\n", "TestOutput1.itl").internal(true).build();
142+
Source source2 = Source.newBuilder(InstrumentationTestLanguage.ID, "ROOT(\n" +
143+
" PRINT(OUT, \"Text to stdout\n\"),\n" +
144+
" PRINT(ERR, \"Text to stderr\n\")" +
145+
")\n", "TestOutput2.itl").build();
146+
Source source3 = Source.newBuilder(InstrumentationTestLanguage.ID, "ROOT(\n" +
147+
" PRINT(OUT, \"Epilogue to stdout\n\"),\n" +
148+
" PRINT(ERR, \"Epilogue to stderr\n\")" +
149+
")\n", "TestOutput3.itl").build();
150+
tester = DAPTester.start(false, context -> context.eval(source1));
151+
tester.sendMessage(
152+
"{\"command\":\"initialize\",\"arguments\":{\"clientID\":\"DAPTester\",\"clientName\":\"DAP Tester\",\"adapterID\":\"graalvm\",\"pathFormat\":\"path\",\"linesStartAt1\":true,\"columnsStartAt1\":true," +
153+
"\"supportsVariableType\":true,\"supportsVariablePaging\":true,\"supportsRunInTerminalRequest\":true,\"locale\":\"en-us\",\"supportsProgressReporting\":true},\"type\":\"request\",\"seq\":1}");
154+
tester.compareReceivedMessages(
155+
"{\"event\":\"initialized\",\"type\":\"event\"}",
156+
"{\"success\":true,\"type\":\"response\",\"body\":{\"supportsConditionalBreakpoints\":true,\"supportsLoadedSourcesRequest\":true,\"supportsFunctionBreakpoints\":true,\"supportsExceptionInfoRequest\":true," +
157+
"\"supportsBreakpointLocationsRequest\":true,\"supportsHitConditionalBreakpoints\":true,\"supportsLogPoints\":true,\"supportsSetVariable\":true,\"supportsConfigurationDoneRequest\":true," +
158+
"\"exceptionBreakpointFilters\":[{\"filter\":\"all\",\"label\":\"All Exceptions\"},{\"filter\":\"uncaught\",\"label\":\"Uncaught Exceptions\"}]},\"request_seq\":1,\"command\":\"initialize\"}");
159+
tester.sendMessage(
160+
"{\"command\":\"attach\",\"arguments\":{\"type\":\"graalvm\",\"request\":\"attach\",\"name\":\"Attach\",\"port\":9229,\"protocol\":\"debugAdapter\"},\"type\":\"request\",\"seq\":2}");
161+
tester.compareReceivedMessages("{\"event\":\"output\",\"body\":{\"output\":\"Debugger attached.\",\"category\":\"stderr\"},\"type\":\"event\"}",
162+
"{\"success\":true,\"type\":\"response\",\"request_seq\":2,\"command\":\"attach\"}");
163+
tester.sendMessage("{\"command\":\"loadedSources\",\"type\":\"request\",\"seq\":3}");
164+
tester.compareReceivedMessages("{\"success\":true,\"body\":{\"sources\":[]},\"type\":\"response\",\"request_seq\":3,\"command\":\"loadedSources\",\"seq\":5}");
165+
tester.sendMessage("{\"command\":\"configurationDone\",\"type\":\"request\",\"seq\":4}");
166+
tester.compareReceivedMessages("{\"success\":true,\"type\":\"response\",\"request_seq\":4,\"command\":\"configurationDone\",\"seq\":6}");
167+
tester.eval(source2);
168+
tester.compareReceivedMessages("{\"event\":\"thread\",\"body\":{\"threadId\":2,\"reason\":\"started\"},\"type\":\"event\",\"seq\":7}");
169+
tester.compareReceivedMessages(
170+
"{\"event\":\"loadedSource\",\"body\":{\"reason\":\"new\",\"source\":{\"sourceReference\":1,\"name\":\"TestOutput2.itl\"}},\"type\":\"event\",\"seq\":8}");
171+
tester.compareReceivedMessages(
172+
"{\"event\":\"output\",\"body\":{\"output\":\"Text to stdout\\n\",\"category\":\"stdout\"},\"type\":\"event\",\"seq\":9}");
173+
tester.compareReceivedMessages(
174+
"{\"event\":\"output\",\"body\":{\"output\":\"Text to stderr\\n\",\"category\":\"stderr\"},\"type\":\"event\",\"seq\":10}");
175+
tester.sendMessage("{\"command\":\"disconnect\",\"arguments\":{\"restart\":false},\"type\":\"request\",\"seq\":11}");
176+
tester.compareReceivedMessages(
177+
"{\"success\":true,\"type\":\"response\",\"request_seq\":11,\"command\":\"disconnect\",\"seq\":11}");
178+
tester.finish();
179+
tester.getContext().eval(source3);
180+
}
181+
136182
@Test
137183
public void testMultiThreading() throws Exception {
138184
Source source = Source.newBuilder(InstrumentationTestLanguage.ID, new URL("file:///path/TestThreads.itl")).content("ROOT(\n" +

tools/src/com.oracle.truffle.tools.dap/src/com/oracle/truffle/tools/dap/server/DebugProtocolServerImpl.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,12 @@ private class ConsoleOutputListener implements OutputHandler.Listener {
707707

708708
@Override
709709
public void outputText(String text) {
710-
OutputEvent.EventBody event = OutputEvent.EventBody.create(text);
711-
event.setCategory(category);
712-
context.getClient().output(event);
710+
DebugProtocolClient debugClient = context.getClient();
711+
if (client != null) {
712+
OutputEvent.EventBody event = OutputEvent.EventBody.create(text);
713+
event.setCategory(category);
714+
debugClient.output(event);
715+
}
713716
}
714717
}
715718

0 commit comments

Comments
 (0)