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 | |
17 | using namespace llvm; |
18 | |
19 | static 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 | |
32 | namespace lldb_dap::protocol { |
33 | |
34 | enum MessageType : unsigned { |
35 | eMessageTypeRequest, |
36 | eMessageTypeResponse, |
37 | eMessageTypeEvent |
38 | }; |
39 | |
40 | bool 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 | |
60 | json::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 | |
73 | bool 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 | |
101 | json::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 | |
131 | bool 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 | |
150 | bool 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 | |
180 | json::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 | |
201 | bool 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 | |
210 | json::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 | |
223 | bool 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 | |
251 | bool 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 | |
285 | json::Value toJSON(const Message &M) { |
286 | return std::visit(visitor: [](auto &M) { return toJSON(M); }, variants: M); |
287 | } |
288 | |
289 | json::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 | |