1//===-- ProtocolBase.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/ProtocolBase.h"
10#include "llvm/ADT/StringRef.h"
11#include "llvm/ADT/StringSwitch.h"
12#include "llvm/Support/ErrorHandling.h"
13#include "llvm/Support/JSON.h"
14#include <optional>
15#include <utility>
16
17using namespace llvm;
18
19static bool mapRaw(const json::Value &Params, StringLiteral Prop,
20 std::optional<json::Value> &V, json::Path P) {
21 const auto *O = Params.getAsObject();
22 if (!O) {
23 P.report(Message: "expected object");
24 return false;
25 }
26 const json::Value *E = O->get(K: Prop);
27 if (E)
28 V = std::move(*E);
29 return true;
30}
31
32namespace lldb_dap::protocol {
33
34enum MessageType : unsigned {
35 eMessageTypeRequest,
36 eMessageTypeResponse,
37 eMessageTypeEvent
38};
39
40bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
41 auto rawType = Params.getAsString();
42 if (!rawType) {
43 P.report(Message: "expected a string");
44 return false;
45 }
46 std::optional<MessageType> type =
47 StringSwitch<std::optional<MessageType>>(*rawType)
48 .Case(S: "request", Value: eMessageTypeRequest)
49 .Case(S: "response", Value: eMessageTypeResponse)
50 .Case(S: "event", Value: eMessageTypeEvent)
51 .Default(Value: std::nullopt);
52 if (!type) {
53 P.report(Message: "unexpected value, expected 'request', 'response' or 'event'");
54 return false;
55 }
56 M = *type;
57 return true;
58}
59
60json::Value toJSON(const Request &R) {
61 json::Object Result{
62 {.K: "type", .V: "request"},
63 {.K: "seq", .V: R.seq},
64 {.K: "command", .V: R.command},
65 };
66
67 if (R.arguments)
68 Result.insert(E: {.K: "arguments", .V: R.arguments});
69
70 return std::move(Result);
71}
72
73bool fromJSON(json::Value const &Params, Request &R, json::Path P) {
74 json::ObjectMapper O(Params, P);
75 if (!O)
76 return false;
77
78 MessageType type;
79 if (!O.map(Prop: "type", Out&: type) || !O.map(Prop: "command", Out&: R.command) ||
80 !O.map(Prop: "seq", Out&: R.seq))
81 return false;
82
83 if (type != eMessageTypeRequest) {
84 P.field(Field: "type").report(Message: "expected to be 'request'");
85 return false;
86 }
87
88 if (R.command.empty()) {
89 P.field(Field: "command").report(Message: "expected to not be ''");
90 return false;
91 }
92
93 if (!R.seq) {
94 P.field(Field: "seq").report(Message: "expected to not be '0'");
95 return false;
96 }
97
98 return mapRaw(Params, Prop: "arguments", V&: R.arguments, P);
99}
100
101json::Value toJSON(const Response &R) {
102 json::Object Result{{.K: "type", .V: "response"},
103 {.K: "seq", .V: 0},
104 {.K: "command", .V: R.command},
105 {.K: "request_seq", .V: R.request_seq},
106 {.K: "success", .V: R.success}};
107
108 if (R.message) {
109 assert(!R.success && "message can only be used if success is false");
110 if (const auto *messageEnum = std::get_if<ResponseMessage>(ptr: &*R.message)) {
111 switch (*messageEnum) {
112 case eResponseMessageCancelled:
113 Result.insert(E: {.K: "message", .V: "cancelled"});
114 break;
115 case eResponseMessageNotStopped:
116 Result.insert(E: {.K: "message", .V: "notStopped"});
117 break;
118 }
119 } else if (const auto *messageString =
120 std::get_if<std::string>(ptr: &*R.message)) {
121 Result.insert(E: {.K: "message", .V: *messageString});
122 }
123 }
124
125 if (R.body)
126 Result.insert(E: {.K: "body", .V: R.body});
127
128 return std::move(Result);
129}
130
131bool fromJSON(json::Value const &Params,
132 std::variant<ResponseMessage, std::string> &M, json::Path P) {
133 auto rawMessage = Params.getAsString();
134 if (!rawMessage) {
135 P.report(Message: "expected a string");
136 return false;
137 }
138 std::optional<ResponseMessage> message =
139 StringSwitch<std::optional<ResponseMessage>>(*rawMessage)
140 .Case(S: "cancelled", Value: eResponseMessageCancelled)
141 .Case(S: "notStopped", Value: eResponseMessageNotStopped)
142 .Default(Value: std::nullopt);
143 if (message)
144 M = *message;
145 else if (!rawMessage->empty())
146 M = rawMessage->str();
147 return true;
148}
149
150bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
151 json::ObjectMapper O(Params, P);
152 if (!O)
153 return false;
154
155 MessageType type;
156 int64_t seq;
157 if (!O.map(Prop: "type", Out&: type) || !O.map(Prop: "seq", Out&: seq) ||
158 !O.map(Prop: "command", Out&: R.command) || !O.map(Prop: "request_seq", Out&: R.request_seq))
159 return false;
160
161 if (type != eMessageTypeResponse) {
162 P.field(Field: "type").report(Message: "expected to be 'response'");
163 return false;
164 }
165
166 if (R.command.empty()) {
167 P.field(Field: "command").report(Message: "expected to not be ''");
168 return false;
169 }
170
171 if (R.request_seq == 0) {
172 P.field(Field: "request_seq").report(Message: "expected to not be '0'");
173 return false;
174 }
175
176 return O.map(Prop: "success", Out&: R.success) && O.map(Prop: "message", Out&: R.message) &&
177 mapRaw(Params, Prop: "body", V&: R.body, P);
178}
179
180json::Value toJSON(const ErrorMessage &EM) {
181 json::Object Result{{.K: "id", .V: EM.id}, {.K: "format", .V: EM.format}};
182
183 if (EM.variables) {
184 json::Object variables;
185 for (auto &var : *EM.variables)
186 variables[var.first] = var.second;
187 Result.insert(E: {.K: "variables", .V: std::move(variables)});
188 }
189 if (EM.sendTelemetry)
190 Result.insert(E: {.K: "sendTelemetry", .V: EM.sendTelemetry});
191 if (EM.showUser)
192 Result.insert(E: {.K: "showUser", .V: EM.showUser});
193 if (EM.url)
194 Result.insert(E: {.K: "url", .V: EM.url});
195 if (EM.urlLabel)
196 Result.insert(E: {.K: "urlLabel", .V: EM.urlLabel});
197
198 return std::move(Result);
199}
200
201bool fromJSON(json::Value const &Params, ErrorMessage &EM, json::Path P) {
202 json::ObjectMapper O(Params, P);
203 return O && O.map(Prop: "id", Out&: EM.id) && O.map(Prop: "format", Out&: EM.format) &&
204 O.map(Prop: "variables", Out&: EM.variables) &&
205 O.map(Prop: "sendTelemetry", Out&: EM.sendTelemetry) &&
206 O.map(Prop: "showUser", Out&: EM.showUser) && O.map(Prop: "url", Out&: EM.url) &&
207 O.map(Prop: "urlLabel", Out&: EM.urlLabel);
208}
209
210json::Value toJSON(const Event &E) {
211 json::Object Result{
212 {.K: "type", .V: "event"},
213 {.K: "seq", .V: 0},
214 {.K: "event", .V: E.event},
215 };
216
217 if (E.body)
218 Result.insert(E: {.K: "body", .V: E.body});
219
220 return std::move(Result);
221}
222
223bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
224 json::ObjectMapper O(Params, P);
225 if (!O)
226 return false;
227
228 MessageType type;
229 int64_t seq;
230 if (!O.map(Prop: "type", Out&: type) || !O.map(Prop: "seq", Out&: seq) || !O.map(Prop: "event", Out&: E.event))
231 return false;
232
233 if (type != eMessageTypeEvent) {
234 P.field(Field: "type").report(Message: "expected to be 'event'");
235 return false;
236 }
237
238 if (seq != 0) {
239 P.field(Field: "seq").report(Message: "expected to be '0'");
240 return false;
241 }
242
243 if (E.event.empty()) {
244 P.field(Field: "event").report(Message: "expected to not be ''");
245 return false;
246 }
247
248 return mapRaw(Params, Prop: "body", V&: E.body, P);
249}
250
251bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {
252 json::ObjectMapper O(Params, P);
253 if (!O)
254 return false;
255
256 MessageType type;
257 if (!O.map(Prop: "type", Out&: type))
258 return false;
259
260 switch (type) {
261 case eMessageTypeRequest: {
262 Request req;
263 if (!fromJSON(Params, R&: req, P))
264 return false;
265 PM = std::move(req);
266 return true;
267 }
268 case eMessageTypeResponse: {
269 Response resp;
270 if (!fromJSON(Params, R&: resp, P))
271 return false;
272 PM = std::move(resp);
273 return true;
274 }
275 case eMessageTypeEvent:
276 Event evt;
277 if (!fromJSON(Params, E&: evt, P))
278 return false;
279 PM = std::move(evt);
280 return true;
281 }
282 llvm_unreachable("unhandled message type request.");
283}
284
285json::Value toJSON(const Message &M) {
286 return std::visit(visitor: [](auto &M) { return toJSON(M); }, variants: M);
287}
288
289json::Value toJSON(const ErrorResponseBody &E) {
290 json::Object result{};
291
292 if (E.error)
293 result.insert(E: {.K: "error", .V: *E.error});
294
295 return result;
296}
297
298} // namespace lldb_dap::protocol
299

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