1//===-- ProtocolRequests.cpp ----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Protocol/ProtocolRequests.h"
10#include "llvm/ADT/DenseMap.h"
11#include "llvm/ADT/StringMap.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/Support/JSON.h"
14#include <utility>
15
16using namespace llvm;
17
18// The 'env' field is either an object as a map of strings or as an array of
19// strings formatted like 'key=value'.
20static bool parseEnv(const json::Value &Params, StringMap<std::string> &env,
21 json::Path P) {
22 const json::Object *O = Params.getAsObject();
23 if (!O) {
24 P.report(Message: "expected object");
25 return false;
26 }
27
28 const json::Value *value = O->get(K: "env");
29 if (!value)
30 return true;
31
32 if (const json::Object *env_obj = value->getAsObject()) {
33 for (const auto &kv : *env_obj) {
34 const std::optional<StringRef> value = kv.second.getAsString();
35 if (!value) {
36 P.field(Field: "env").field(Field: kv.first).report(Message: "expected string value");
37 return false;
38 }
39 env.insert(KV: {kv.first.str(), value->str()});
40 }
41 return true;
42 }
43
44 if (const json::Array *env_arr = value->getAsArray()) {
45 for (size_t i = 0; i < env_arr->size(); ++i) {
46 const std::optional<StringRef> value = (*env_arr)[i].getAsString();
47 if (!value) {
48 P.field(Field: "env").index(Index: i).report(Message: "expected string");
49 return false;
50 }
51 std::pair<StringRef, StringRef> kv = value->split(Separator: "=");
52 env.insert(KV: {kv.first, kv.second.str()});
53 }
54
55 return true;
56 }
57
58 P.field(Field: "env").report(Message: "invalid format, expected array or object");
59 return false;
60}
61
62static bool parseTimeout(const json::Value &Params, std::chrono::seconds &S,
63 json::Path P) {
64 const json::Object *O = Params.getAsObject();
65 if (!O) {
66 P.report(Message: "expected object");
67 return false;
68 }
69
70 const json::Value *value = O->get(K: "timeout");
71 if (!value)
72 return true;
73 std::optional<double> timeout = value->getAsNumber();
74 if (!timeout) {
75 P.field(Field: "timeout").report(Message: "expected number");
76 return false;
77 }
78
79 S = std::chrono::duration_cast<std::chrono::seconds>(
80 d: std::chrono::duration<double>(*value->getAsNumber()));
81 return true;
82}
83
84static bool
85parseSourceMap(const json::Value &Params,
86 std::vector<std::pair<std::string, std::string>> &sourceMap,
87 json::Path P) {
88 const json::Object *O = Params.getAsObject();
89 if (!O) {
90 P.report(Message: "expected object");
91 return false;
92 }
93
94 const json::Value *value = O->get(K: "sourceMap");
95 if (!value)
96 return true;
97
98 if (const json::Object *map_obj = value->getAsObject()) {
99 for (const auto &kv : *map_obj) {
100 const std::optional<StringRef> value = kv.second.getAsString();
101 if (!value) {
102 P.field(Field: "sourceMap").field(Field: kv.first).report(Message: "expected string value");
103 return false;
104 }
105 sourceMap.emplace_back(args: std::make_pair(x: kv.first.str(), y: value->str()));
106 }
107 return true;
108 }
109
110 if (const json::Array *env_arr = value->getAsArray()) {
111 for (size_t i = 0; i < env_arr->size(); ++i) {
112 const json::Array *kv = (*env_arr)[i].getAsArray();
113 if (!kv) {
114 P.field(Field: "sourceMap").index(Index: i).report(Message: "expected array");
115 return false;
116 }
117 if (kv->size() != 2) {
118 P.field(Field: "sourceMap").index(Index: i).report(Message: "expected array of pairs");
119 return false;
120 }
121 const std::optional<StringRef> first = (*kv)[0].getAsString();
122 if (!first) {
123 P.field(Field: "sourceMap").index(Index: 0).report(Message: "expected string");
124 return false;
125 }
126 const std::optional<StringRef> second = (*kv)[1].getAsString();
127 if (!second) {
128 P.field(Field: "sourceMap").index(Index: 1).report(Message: "expected string");
129 return false;
130 }
131 sourceMap.emplace_back(args: std::make_pair(x: *first, y: second->str()));
132 }
133
134 return true;
135 }
136
137 P.report(Message: "invalid format, expected array or object");
138 return false;
139}
140
141namespace lldb_dap::protocol {
142
143bool fromJSON(const json::Value &Params, CancelArguments &CA, json::Path P) {
144 json::ObjectMapper O(Params, P);
145 return O && O.map(Prop: "requestId", Out&: CA.requestId) &&
146 O.map(Prop: "progressId", Out&: CA.progressId);
147}
148
149bool fromJSON(const json::Value &Params, DisconnectArguments &DA,
150 json::Path P) {
151 json::ObjectMapper O(Params, P);
152 return O && O.mapOptional(Prop: "restart", Out&: DA.restart) &&
153 O.mapOptional(Prop: "terminateDebuggee", Out&: DA.terminateDebuggee) &&
154 O.mapOptional(Prop: "suspendDebuggee", Out&: DA.suspendDebuggee);
155}
156
157bool fromJSON(const json::Value &Params, PathFormat &PF, json::Path P) {
158 auto rawPathFormat = Params.getAsString();
159 if (!rawPathFormat) {
160 P.report(Message: "expected a string");
161 return false;
162 }
163
164 std::optional<PathFormat> pathFormat =
165 StringSwitch<std::optional<PathFormat>>(*rawPathFormat)
166 .Case(S: "path", Value: ePatFormatPath)
167 .Case(S: "uri", Value: ePathFormatURI)
168 .Default(Value: std::nullopt);
169 if (!pathFormat) {
170 P.report(Message: "unexpected value, expected 'path' or 'uri'");
171 return false;
172 }
173
174 PF = *pathFormat;
175 return true;
176}
177
178static const StringMap<ClientFeature> ClientFeatureByKey{
179 {"supportsVariableType", eClientFeatureVariableType},
180 {"supportsVariablePaging", eClientFeatureVariablePaging},
181 {"supportsRunInTerminalRequest", eClientFeatureRunInTerminalRequest},
182 {"supportsMemoryReferences", eClientFeatureMemoryReferences},
183 {"supportsProgressReporting", eClientFeatureProgressReporting},
184 {"supportsInvalidatedEvent", eClientFeatureInvalidatedEvent},
185 {"supportsMemoryEvent", eClientFeatureMemoryEvent},
186 {"supportsArgsCanBeInterpretedByShell",
187 eClientFeatureArgsCanBeInterpretedByShell},
188 {"supportsStartDebuggingRequest", eClientFeatureStartDebuggingRequest},
189 {"supportsANSIStyling", eClientFeatureANSIStyling}};
190
191bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA,
192 json::Path P) {
193 json::ObjectMapper OM(Params, P);
194 if (!OM)
195 return false;
196
197 const json::Object *O = Params.getAsObject();
198
199 for (auto &kv : ClientFeatureByKey) {
200 const json::Value *value_ref = O->get(K: kv.first());
201 if (!value_ref)
202 continue;
203
204 const std::optional<bool> value = value_ref->getAsBoolean();
205 if (!value) {
206 P.field(Field: kv.first()).report(Message: "expected bool");
207 return false;
208 }
209
210 if (*value)
211 IRA.supportedFeatures.insert(V: kv.second);
212 }
213
214 return OM.map(Prop: "adapterID", Out&: IRA.adapterID) &&
215 OM.map(Prop: "clientID", Out&: IRA.clientID) &&
216 OM.map(Prop: "clientName", Out&: IRA.clientName) && OM.map(Prop: "locale", Out&: IRA.locale) &&
217 OM.map(Prop: "linesStartAt1", Out&: IRA.linesStartAt1) &&
218 OM.map(Prop: "columnsStartAt1", Out&: IRA.columnsStartAt1) &&
219 OM.map(Prop: "pathFormat", Out&: IRA.pathFormat) &&
220 OM.map(Prop: "$__lldb_sourceInitFile", Out&: IRA.lldbExtSourceInitFile);
221}
222
223bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) {
224 json::ObjectMapper O(Params, P);
225 return O.mapOptional(Prop: "debuggerRoot", Out&: C.debuggerRoot) &&
226 O.mapOptional(Prop: "enableAutoVariableSummaries",
227 Out&: C.enableAutoVariableSummaries) &&
228 O.mapOptional(Prop: "enableSyntheticChildDebugging",
229 Out&: C.enableSyntheticChildDebugging) &&
230 O.mapOptional(Prop: "displayExtendedBacktrace",
231 Out&: C.displayExtendedBacktrace) &&
232 O.mapOptional(Prop: "stopOnEntry", Out&: C.stopOnEntry) &&
233 O.mapOptional(Prop: "commandEscapePrefix", Out&: C.commandEscapePrefix) &&
234 O.mapOptional(Prop: "customFrameFormat", Out&: C.customFrameFormat) &&
235 O.mapOptional(Prop: "customThreadFormat", Out&: C.customThreadFormat) &&
236 O.mapOptional(Prop: "sourcePath", Out&: C.sourcePath) &&
237 O.mapOptional(Prop: "initCommands", Out&: C.initCommands) &&
238 O.mapOptional(Prop: "preRunCommands", Out&: C.preRunCommands) &&
239 O.mapOptional(Prop: "postRunCommands", Out&: C.postRunCommands) &&
240 O.mapOptional(Prop: "stopCommands", Out&: C.stopCommands) &&
241 O.mapOptional(Prop: "exitCommands", Out&: C.exitCommands) &&
242 O.mapOptional(Prop: "terminateCommands", Out&: C.terminateCommands) &&
243 O.mapOptional(Prop: "program", Out&: C.program) &&
244 O.mapOptional(Prop: "targetTriple", Out&: C.targetTriple) &&
245 O.mapOptional(Prop: "platformName", Out&: C.platformName) &&
246 parseSourceMap(Params, sourceMap&: C.sourceMap, P) &&
247 parseTimeout(Params, S&: C.timeout, P);
248}
249
250bool fromJSON(const json::Value &Params, BreakpointLocationsArguments &BLA,
251 json::Path P) {
252 json::ObjectMapper O(Params, P);
253 return O && O.map(Prop: "source", Out&: BLA.source) && O.map(Prop: "line", Out&: BLA.line) &&
254 O.mapOptional(Prop: "column", Out&: BLA.column) &&
255 O.mapOptional(Prop: "endLine", Out&: BLA.endLine) &&
256 O.mapOptional(Prop: "endColumn", Out&: BLA.endColumn);
257}
258
259json::Value toJSON(const BreakpointLocationsResponseBody &BLRB) {
260 return json::Object{{.K: "breakpoints", .V: BLRB.breakpoints}};
261}
262
263bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
264 json::Path P) {
265 json::ObjectMapper O(Params, P);
266 return O && fromJSON(Params, C&: LRA.configuration, P) &&
267 O.mapOptional(Prop: "noDebug", Out&: LRA.noDebug) &&
268 O.mapOptional(Prop: "launchCommands", Out&: LRA.launchCommands) &&
269 O.mapOptional(Prop: "cwd", Out&: LRA.cwd) && O.mapOptional(Prop: "args", Out&: LRA.args) &&
270 O.mapOptional(Prop: "detachOnError", Out&: LRA.detachOnError) &&
271 O.mapOptional(Prop: "disableASLR", Out&: LRA.disableASLR) &&
272 O.mapOptional(Prop: "disableSTDIO", Out&: LRA.disableSTDIO) &&
273 O.mapOptional(Prop: "shellExpandArguments", Out&: LRA.shellExpandArguments) &&
274
275 O.mapOptional(Prop: "runInTerminal", Out&: LRA.runInTerminal) &&
276 parseEnv(Params, env&: LRA.env, P);
277}
278
279bool fromJSON(const json::Value &Params, AttachRequestArguments &ARA,
280 json::Path P) {
281 json::ObjectMapper O(Params, P);
282 return O && fromJSON(Params, C&: ARA.configuration, P) &&
283 O.mapOptional(Prop: "attachCommands", Out&: ARA.attachCommands) &&
284 O.mapOptional(Prop: "pid", Out&: ARA.pid) &&
285 O.mapOptional(Prop: "waitFor", Out&: ARA.waitFor) &&
286 O.mapOptional(Prop: "gdb-remote-port", Out&: ARA.gdbRemotePort) &&
287 O.mapOptional(Prop: "gdb-remote-hostname", Out&: ARA.gdbRemoteHostname) &&
288 O.mapOptional(Prop: "coreFile", Out&: ARA.coreFile);
289}
290
291bool fromJSON(const json::Value &Params, ContinueArguments &CA, json::Path P) {
292 json::ObjectMapper O(Params, P);
293 return O && O.map(Prop: "threadId", Out&: CA.threadId) &&
294 O.mapOptional(Prop: "singleThread", Out&: CA.singleThread);
295}
296
297json::Value toJSON(const ContinueResponseBody &CRB) {
298 json::Object Body{{.K: "allThreadsContinued", .V: CRB.allThreadsContinued}};
299 return std::move(Body);
300}
301
302bool fromJSON(const json::Value &Params, SetVariableArguments &SVA,
303 json::Path P) {
304 json::ObjectMapper O(Params, P);
305 return O && O.map(Prop: "variablesReference", Out&: SVA.variablesReference) &&
306 O.map(Prop: "name", Out&: SVA.name) && O.map(Prop: "value", Out&: SVA.value) &&
307 O.mapOptional(Prop: "format", Out&: SVA.format);
308}
309
310json::Value toJSON(const SetVariableResponseBody &SVR) {
311 json::Object Body{{.K: "value", .V: SVR.value}};
312 if (SVR.type.has_value())
313 Body.insert(E: {.K: "type", .V: SVR.type});
314
315 if (SVR.variablesReference.has_value())
316 Body.insert(E: {.K: "variablesReference", .V: SVR.variablesReference});
317
318 if (SVR.namedVariables.has_value())
319 Body.insert(E: {.K: "namedVariables", .V: SVR.namedVariables});
320
321 if (SVR.indexedVariables.has_value())
322 Body.insert(E: {.K: "indexedVariables", .V: SVR.indexedVariables});
323
324 if (SVR.memoryReference.has_value())
325 Body.insert(E: {.K: "memoryReference", .V: SVR.memoryReference});
326
327 if (SVR.valueLocationReference.has_value())
328 Body.insert(E: {.K: "valueLocationReference", .V: SVR.valueLocationReference});
329
330 return json::Value(std::move(Body));
331}
332bool fromJSON(const json::Value &Params, ScopesArguments &SCA, json::Path P) {
333 json::ObjectMapper O(Params, P);
334 return O && O.map(Prop: "frameId", Out&: SCA.frameId);
335}
336
337json::Value toJSON(const ScopesResponseBody &SCR) {
338 return json::Object{{.K: "scopes", .V: SCR.scopes}};
339}
340
341bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
342 json::ObjectMapper O(Params, P);
343 return O && O.map(Prop: "source", Out&: SA.source) &&
344 O.map(Prop: "sourceReference", Out&: SA.sourceReference);
345}
346
347json::Value toJSON(const SourceResponseBody &SA) {
348 json::Object Result{{.K: "content", .V: SA.content}};
349
350 if (SA.mimeType)
351 Result.insert(E: {.K: "mimeType", .V: SA.mimeType});
352
353 return std::move(Result);
354}
355
356bool fromJSON(const json::Value &Params, NextArguments &NA, json::Path P) {
357 json::ObjectMapper OM(Params, P);
358 return OM && OM.map(Prop: "threadId", Out&: NA.threadId) &&
359 OM.mapOptional(Prop: "singleThread", Out&: NA.singleThread) &&
360 OM.mapOptional(Prop: "granularity", Out&: NA.granularity);
361}
362
363bool fromJSON(const json::Value &Params, StepInArguments &SIA, json::Path P) {
364 json::ObjectMapper OM(Params, P);
365 return OM && OM.map(Prop: "threadId", Out&: SIA.threadId) &&
366 OM.map(Prop: "targetId", Out&: SIA.targetId) &&
367 OM.mapOptional(Prop: "singleThread", Out&: SIA.singleThread) &&
368 OM.mapOptional(Prop: "granularity", Out&: SIA.granularity);
369}
370
371bool fromJSON(const json::Value &Params, StepOutArguments &SOA, json::Path P) {
372 json::ObjectMapper OM(Params, P);
373 return OM && OM.map(Prop: "threadId", Out&: SOA.threadId) &&
374 OM.mapOptional(Prop: "singleThread", Out&: SOA.singleThread) &&
375 OM.mapOptional(Prop: "granularity", Out&: SOA.granularity);
376}
377
378bool fromJSON(const json::Value &Params, SetBreakpointsArguments &SBA,
379 json::Path P) {
380 json::ObjectMapper O(Params, P);
381 return O && O.map(Prop: "source", Out&: SBA.source) &&
382 O.map(Prop: "breakpoints", Out&: SBA.breakpoints) && O.map(Prop: "lines", Out&: SBA.lines) &&
383 O.map(Prop: "sourceModified", Out&: SBA.sourceModified);
384}
385
386json::Value toJSON(const SetBreakpointsResponseBody &SBR) {
387 return json::Object{{.K: "breakpoints", .V: SBR.breakpoints}};
388}
389
390bool fromJSON(const json::Value &Params, SetFunctionBreakpointsArguments &SFBA,
391 json::Path P) {
392 json::ObjectMapper O(Params, P);
393 return O && O.map(Prop: "breakpoints", Out&: SFBA.breakpoints);
394}
395
396json::Value toJSON(const SetFunctionBreakpointsResponseBody &SFBR) {
397 return json::Object{{.K: "breakpoints", .V: SFBR.breakpoints}};
398}
399
400bool fromJSON(const json::Value &Params,
401 SetInstructionBreakpointsArguments &SIBA, json::Path P) {
402 json::ObjectMapper O(Params, P);
403 return O && O.map(Prop: "breakpoints", Out&: SIBA.breakpoints);
404}
405
406json::Value toJSON(const SetInstructionBreakpointsResponseBody &SIBR) {
407 return json::Object{{.K: "breakpoints", .V: SIBR.breakpoints}};
408}
409
410bool fromJSON(const json::Value &Params, DataBreakpointInfoArguments &DBIA,
411 json::Path P) {
412 json::ObjectMapper O(Params, P);
413 return O && O.map(Prop: "variablesReference", Out&: DBIA.variablesReference) &&
414 O.map(Prop: "name", Out&: DBIA.name) && O.map(Prop: "frameId", Out&: DBIA.frameId) &&
415 O.map(Prop: "bytes", Out&: DBIA.bytes) && O.map(Prop: "asAddress", Out&: DBIA.asAddress) &&
416 O.map(Prop: "mode", Out&: DBIA.mode);
417}
418
419json::Value toJSON(const DataBreakpointInfoResponseBody &DBIRB) {
420 json::Object result{{.K: "dataId", .V: DBIRB.dataId},
421 {.K: "description", .V: DBIRB.description}};
422
423 if (DBIRB.accessTypes)
424 result["accessTypes"] = *DBIRB.accessTypes;
425 if (DBIRB.canPersist)
426 result["canPersist"] = *DBIRB.canPersist;
427
428 return result;
429}
430
431bool fromJSON(const json::Value &Params, SetDataBreakpointsArguments &SDBA,
432 json::Path P) {
433 json::ObjectMapper O(Params, P);
434 return O && O.map(Prop: "breakpoints", Out&: SDBA.breakpoints);
435}
436
437json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) {
438 return json::Object{{.K: "breakpoints", .V: SDBR.breakpoints}};
439}
440
441json::Value toJSON(const ThreadsResponseBody &TR) {
442 return json::Object{{.K: "threads", .V: TR.threads}};
443}
444
445bool fromJSON(const llvm::json::Value &Params, DisassembleArguments &DA,
446 llvm::json::Path P) {
447 json::ObjectMapper O(Params, P);
448 return O && O.map(Prop: "memoryReference", Out&: DA.memoryReference) &&
449 O.mapOptional(Prop: "offset", Out&: DA.offset) &&
450 O.mapOptional(Prop: "instructionOffset", Out&: DA.instructionOffset) &&
451 O.map(Prop: "instructionCount", Out&: DA.instructionCount) &&
452 O.mapOptional(Prop: "resolveSymbols", Out&: DA.resolveSymbols);
453}
454
455json::Value toJSON(const DisassembleResponseBody &DRB) {
456 return json::Object{{.K: "instructions", .V: DRB.instructions}};
457}
458
459} // namespace lldb_dap::protocol
460

source code of lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp