1 | //===-- ProtocolTypes.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/ProtocolTypes.h" |
10 | #include "JSONUtils.h" |
11 | #include "lldb/lldb-types.h" |
12 | #include "llvm/ADT/StringExtras.h" |
13 | #include "llvm/ADT/StringRef.h" |
14 | #include "llvm/Support/ErrorHandling.h" |
15 | #include "llvm/Support/JSON.h" |
16 | #include <optional> |
17 | |
18 | using namespace llvm; |
19 | |
20 | namespace lldb_dap::protocol { |
21 | |
22 | bool fromJSON(const json::Value &Params, Source::PresentationHint &PH, |
23 | json::Path P) { |
24 | auto rawHint = Params.getAsString(); |
25 | if (!rawHint) { |
26 | P.report(Message: "expected a string" ); |
27 | return false; |
28 | } |
29 | std::optional<Source::PresentationHint> hint = |
30 | StringSwitch<std::optional<Source::PresentationHint>>(*rawHint) |
31 | .Case(S: "normal" , Value: Source::eSourcePresentationHintNormal) |
32 | .Case(S: "emphasize" , Value: Source::eSourcePresentationHintEmphasize) |
33 | .Case(S: "deemphasize" , Value: Source::eSourcePresentationHintDeemphasize) |
34 | .Default(Value: std::nullopt); |
35 | if (!hint) { |
36 | P.report(Message: "unexpected value" ); |
37 | return false; |
38 | } |
39 | PH = *hint; |
40 | return true; |
41 | } |
42 | |
43 | bool fromJSON(const json::Value &Params, Source &S, json::Path P) { |
44 | json::ObjectMapper O(Params, P); |
45 | return O && O.map(Prop: "name" , Out&: S.name) && O.map(Prop: "path" , Out&: S.path) && |
46 | O.map(Prop: "presentationHint" , Out&: S.presentationHint) && |
47 | O.map(Prop: "sourceReference" , Out&: S.sourceReference); |
48 | } |
49 | |
50 | llvm::json::Value toJSON(Source::PresentationHint hint) { |
51 | switch (hint) { |
52 | case Source::eSourcePresentationHintNormal: |
53 | return "normal" ; |
54 | case Source::eSourcePresentationHintEmphasize: |
55 | return "emphasize" ; |
56 | case Source::eSourcePresentationHintDeemphasize: |
57 | return "deemphasize" ; |
58 | } |
59 | llvm_unreachable("unhandled presentation hint." ); |
60 | } |
61 | |
62 | llvm::json::Value toJSON(const Source &S) { |
63 | json::Object result; |
64 | if (S.name) |
65 | result.insert(E: {.K: "name" , .V: *S.name}); |
66 | if (S.path) |
67 | result.insert(E: {.K: "path" , .V: *S.path}); |
68 | if (S.sourceReference) |
69 | result.insert(E: {.K: "sourceReference" , .V: *S.sourceReference}); |
70 | if (S.presentationHint) |
71 | result.insert(E: {.K: "presentationHint" , .V: *S.presentationHint}); |
72 | |
73 | return result; |
74 | } |
75 | |
76 | bool fromJSON(const llvm::json::Value &Params, ExceptionBreakpointsFilter &EBF, |
77 | llvm::json::Path P) { |
78 | json::ObjectMapper O(Params, P); |
79 | return O && O.map(Prop: "filter" , Out&: EBF.filter) && O.map(Prop: "label" , Out&: EBF.label) && |
80 | O.mapOptional(Prop: "description" , Out&: EBF.description) && |
81 | O.mapOptional(Prop: "default" , Out&: EBF.defaultState) && |
82 | O.mapOptional(Prop: "supportsCondition" , Out&: EBF.supportsCondition) && |
83 | O.mapOptional(Prop: "conditionDescription" , Out&: EBF.conditionDescription); |
84 | } |
85 | |
86 | json::Value toJSON(const ExceptionBreakpointsFilter &EBF) { |
87 | json::Object result{{.K: "filter" , .V: EBF.filter}, {.K: "label" , .V: EBF.label}}; |
88 | |
89 | if (EBF.description) |
90 | result.insert(E: {.K: "description" , .V: *EBF.description}); |
91 | if (EBF.defaultState) |
92 | result.insert(E: {.K: "default" , .V: *EBF.defaultState}); |
93 | if (EBF.supportsCondition) |
94 | result.insert(E: {.K: "supportsCondition" , .V: *EBF.supportsCondition}); |
95 | if (EBF.conditionDescription) |
96 | result.insert(E: {.K: "conditionDescription" , .V: *EBF.conditionDescription}); |
97 | |
98 | return result; |
99 | } |
100 | |
101 | bool fromJSON(const json::Value &Params, ColumnType &CT, json::Path P) { |
102 | auto rawColumnType = Params.getAsString(); |
103 | if (!rawColumnType) { |
104 | P.report(Message: "expected a string" ); |
105 | return false; |
106 | } |
107 | std::optional<ColumnType> columnType = |
108 | StringSwitch<std::optional<ColumnType>>(*rawColumnType) |
109 | .Case(S: "string" , Value: eColumnTypeString) |
110 | .Case(S: "number" , Value: eColumnTypeNumber) |
111 | .Case(S: "boolean" , Value: eColumnTypeBoolean) |
112 | .Case(S: "unixTimestampUTC" , Value: eColumnTypeTimestamp) |
113 | .Default(Value: std::nullopt); |
114 | if (!columnType) { |
115 | P.report(Message: "unexpected value, expected 'string', 'number', 'boolean', or " |
116 | "'unixTimestampUTC'" ); |
117 | return false; |
118 | } |
119 | CT = *columnType; |
120 | return true; |
121 | } |
122 | |
123 | json::Value toJSON(const ColumnType &T) { |
124 | switch (T) { |
125 | case eColumnTypeString: |
126 | return "string" ; |
127 | case eColumnTypeNumber: |
128 | return "number" ; |
129 | case eColumnTypeBoolean: |
130 | return "boolean" ; |
131 | case eColumnTypeTimestamp: |
132 | return "unixTimestampUTC" ; |
133 | } |
134 | llvm_unreachable("unhandled column type." ); |
135 | } |
136 | |
137 | bool fromJSON(const llvm::json::Value &Params, ColumnDescriptor &CD, |
138 | llvm::json::Path P) { |
139 | llvm::json::ObjectMapper O(Params, P); |
140 | return O && O.map(Prop: "attributeName" , Out&: CD.attributeName) && |
141 | O.map(Prop: "label" , Out&: CD.label) && O.mapOptional(Prop: "format" , Out&: CD.format) && |
142 | O.mapOptional(Prop: "type" , Out&: CD.type) && O.mapOptional(Prop: "width" , Out&: CD.width); |
143 | } |
144 | |
145 | json::Value toJSON(const ColumnDescriptor &CD) { |
146 | json::Object result{{.K: "attributeName" , .V: CD.attributeName}, {.K: "label" , .V: CD.label}}; |
147 | |
148 | if (CD.format) |
149 | result.insert(E: {.K: "format" , .V: *CD.format}); |
150 | if (CD.type) |
151 | result.insert(E: {.K: "type" , .V: *CD.type}); |
152 | if (CD.width) |
153 | result.insert(E: {.K: "width" , .V: *CD.width}); |
154 | |
155 | return result; |
156 | } |
157 | |
158 | json::Value toJSON(const ChecksumAlgorithm &CA) { |
159 | switch (CA) { |
160 | case eChecksumAlgorithmMD5: |
161 | return "MD5" ; |
162 | case eChecksumAlgorithmSHA1: |
163 | return "SHA1" ; |
164 | case eChecksumAlgorithmSHA256: |
165 | return "SHA256" ; |
166 | case eChecksumAlgorithmTimestamp: |
167 | return "timestamp" ; |
168 | } |
169 | llvm_unreachable("unhandled checksum algorithm." ); |
170 | } |
171 | |
172 | bool fromJSON(const llvm::json::Value &Params, ChecksumAlgorithm &CA, |
173 | llvm::json::Path P) { |
174 | auto rawAlgorithm = Params.getAsString(); |
175 | if (!rawAlgorithm) { |
176 | P.report(Message: "expected a string" ); |
177 | return false; |
178 | } |
179 | |
180 | std::optional<ChecksumAlgorithm> algorithm = |
181 | llvm::StringSwitch<std::optional<ChecksumAlgorithm>>(*rawAlgorithm) |
182 | .Case(S: "MD5" , Value: eChecksumAlgorithmMD5) |
183 | .Case(S: "SHA1" , Value: eChecksumAlgorithmSHA1) |
184 | .Case(S: "SHA256" , Value: eChecksumAlgorithmSHA256) |
185 | .Case(S: "timestamp" , Value: eChecksumAlgorithmTimestamp) |
186 | .Default(Value: std::nullopt); |
187 | |
188 | if (!algorithm) { |
189 | P.report( |
190 | Message: "unexpected value, expected 'MD5', 'SHA1', 'SHA256', or 'timestamp'" ); |
191 | return false; |
192 | } |
193 | |
194 | CA = *algorithm; |
195 | return true; |
196 | } |
197 | |
198 | json::Value toJSON(const BreakpointModeApplicability &BMA) { |
199 | switch (BMA) { |
200 | case eBreakpointModeApplicabilitySource: |
201 | return "source" ; |
202 | case eBreakpointModeApplicabilityException: |
203 | return "exception" ; |
204 | case eBreakpointModeApplicabilityData: |
205 | return "data" ; |
206 | case eBreakpointModeApplicabilityInstruction: |
207 | return "instruction" ; |
208 | } |
209 | llvm_unreachable("unhandled breakpoint mode applicability." ); |
210 | } |
211 | |
212 | bool fromJSON(const llvm::json::Value &Params, BreakpointModeApplicability &BMA, |
213 | llvm::json::Path P) { |
214 | auto rawApplicability = Params.getAsString(); |
215 | if (!rawApplicability) { |
216 | P.report(Message: "expected a string" ); |
217 | return false; |
218 | } |
219 | std::optional<BreakpointModeApplicability> applicability = |
220 | llvm::StringSwitch<std::optional<BreakpointModeApplicability>>( |
221 | *rawApplicability) |
222 | .Case(S: "source" , Value: eBreakpointModeApplicabilitySource) |
223 | .Case(S: "exception" , Value: eBreakpointModeApplicabilityException) |
224 | .Case(S: "data" , Value: eBreakpointModeApplicabilityData) |
225 | .Case(S: "instruction" , Value: eBreakpointModeApplicabilityInstruction) |
226 | .Default(Value: std::nullopt); |
227 | if (!applicability) { |
228 | P.report(Message: "unexpected value, expected 'source', 'exception', 'data', or " |
229 | "'instruction'" ); |
230 | return false; |
231 | } |
232 | BMA = *applicability; |
233 | return true; |
234 | } |
235 | |
236 | json::Value toJSON(const BreakpointMode &BM) { |
237 | json::Object result{ |
238 | {.K: "mode" , .V: BM.mode}, |
239 | {.K: "label" , .V: BM.label}, |
240 | {.K: "appliesTo" , .V: BM.appliesTo}, |
241 | }; |
242 | |
243 | if (BM.description) |
244 | result.insert(E: {.K: "description" , .V: *BM.description}); |
245 | |
246 | return result; |
247 | } |
248 | |
249 | bool fromJSON(const llvm::json::Value &Params, BreakpointMode &BM, |
250 | llvm::json::Path P) { |
251 | llvm::json::ObjectMapper O(Params, P); |
252 | return O && O.map(Prop: "mode" , Out&: BM.mode) && O.map(Prop: "label" , Out&: BM.label) && |
253 | O.mapOptional(Prop: "description" , Out&: BM.description) && |
254 | O.map(Prop: "appliesTo" , Out&: BM.appliesTo); |
255 | } |
256 | |
257 | static llvm::StringLiteral ToString(AdapterFeature feature) { |
258 | switch (feature) { |
259 | case eAdapterFeatureANSIStyling: |
260 | return "supportsANSIStyling" ; |
261 | case eAdapterFeatureBreakpointLocationsRequest: |
262 | return "supportsBreakpointLocationsRequest" ; |
263 | case eAdapterFeatureCancelRequest: |
264 | return "supportsCancelRequest" ; |
265 | case eAdapterFeatureClipboardContext: |
266 | return "supportsClipboardContext" ; |
267 | case eAdapterFeatureCompletionsRequest: |
268 | return "supportsCompletionsRequest" ; |
269 | case eAdapterFeatureConditionalBreakpoints: |
270 | return "supportsConditionalBreakpoints" ; |
271 | case eAdapterFeatureConfigurationDoneRequest: |
272 | return "supportsConfigurationDoneRequest" ; |
273 | case eAdapterFeatureDataBreakpointBytes: |
274 | return "supportsDataBreakpointBytes" ; |
275 | case eAdapterFeatureDataBreakpoints: |
276 | return "supportsDataBreakpoints" ; |
277 | case eAdapterFeatureDelayedStackTraceLoading: |
278 | return "supportsDelayedStackTraceLoading" ; |
279 | case eAdapterFeatureDisassembleRequest: |
280 | return "supportsDisassembleRequest" ; |
281 | case eAdapterFeatureEvaluateForHovers: |
282 | return "supportsEvaluateForHovers" ; |
283 | case eAdapterFeatureExceptionFilterOptions: |
284 | return "supportsExceptionFilterOptions" ; |
285 | case eAdapterFeatureExceptionInfoRequest: |
286 | return "supportsExceptionInfoRequest" ; |
287 | case eAdapterFeatureExceptionOptions: |
288 | return "supportsExceptionOptions" ; |
289 | case eAdapterFeatureFunctionBreakpoints: |
290 | return "supportsFunctionBreakpoints" ; |
291 | case eAdapterFeatureGotoTargetsRequest: |
292 | return "supportsGotoTargetsRequest" ; |
293 | case eAdapterFeatureHitConditionalBreakpoints: |
294 | return "supportsHitConditionalBreakpoints" ; |
295 | case eAdapterFeatureInstructionBreakpoints: |
296 | return "supportsInstructionBreakpoints" ; |
297 | case eAdapterFeatureLoadedSourcesRequest: |
298 | return "supportsLoadedSourcesRequest" ; |
299 | case eAdapterFeatureLogPoints: |
300 | return "supportsLogPoints" ; |
301 | case eAdapterFeatureModulesRequest: |
302 | return "supportsModulesRequest" ; |
303 | case eAdapterFeatureReadMemoryRequest: |
304 | return "supportsReadMemoryRequest" ; |
305 | case eAdapterFeatureRestartFrame: |
306 | return "supportsRestartFrame" ; |
307 | case eAdapterFeatureRestartRequest: |
308 | return "supportsRestartRequest" ; |
309 | case eAdapterFeatureSetExpression: |
310 | return "supportsSetExpression" ; |
311 | case eAdapterFeatureSetVariable: |
312 | return "supportsSetVariable" ; |
313 | case eAdapterFeatureSingleThreadExecutionRequests: |
314 | return "supportsSingleThreadExecutionRequests" ; |
315 | case eAdapterFeatureStepBack: |
316 | return "supportsStepBack" ; |
317 | case eAdapterFeatureStepInTargetsRequest: |
318 | return "supportsStepInTargetsRequest" ; |
319 | case eAdapterFeatureSteppingGranularity: |
320 | return "supportsSteppingGranularity" ; |
321 | case eAdapterFeatureTerminateRequest: |
322 | return "supportsTerminateRequest" ; |
323 | case eAdapterFeatureTerminateThreadsRequest: |
324 | return "supportsTerminateThreadsRequest" ; |
325 | case eAdapterFeatureSuspendDebuggee: |
326 | return "supportSuspendDebuggee" ; |
327 | case eAdapterFeatureValueFormattingOptions: |
328 | return "supportsValueFormattingOptions" ; |
329 | case eAdapterFeatureWriteMemoryRequest: |
330 | return "supportsWriteMemoryRequest" ; |
331 | case eAdapterFeatureTerminateDebuggee: |
332 | return "supportTerminateDebuggee" ; |
333 | } |
334 | llvm_unreachable("unhandled adapter feature." ); |
335 | } |
336 | |
337 | llvm::json::Value toJSON(const AdapterFeature &feature) { |
338 | return ToString(feature); |
339 | } |
340 | |
341 | bool fromJSON(const llvm::json::Value &Params, AdapterFeature &feature, |
342 | llvm::json::Path P) { |
343 | auto rawFeature = Params.getAsString(); |
344 | if (!rawFeature) { |
345 | P.report(Message: "expected a string" ); |
346 | return false; |
347 | } |
348 | |
349 | std::optional<AdapterFeature> parsedFeature = |
350 | llvm::StringSwitch<std::optional<AdapterFeature>>(*rawFeature) |
351 | .Case(S: "supportsANSIStyling" , Value: eAdapterFeatureANSIStyling) |
352 | .Case(S: "supportsBreakpointLocationsRequest" , |
353 | Value: eAdapterFeatureBreakpointLocationsRequest) |
354 | .Case(S: "supportsCancelRequest" , Value: eAdapterFeatureCancelRequest) |
355 | .Case(S: "supportsClipboardContext" , Value: eAdapterFeatureClipboardContext) |
356 | .Case(S: "supportsCompletionsRequest" , Value: eAdapterFeatureCompletionsRequest) |
357 | .Case(S: "supportsConditionalBreakpoints" , |
358 | Value: eAdapterFeatureConditionalBreakpoints) |
359 | .Case(S: "supportsConfigurationDoneRequest" , |
360 | Value: eAdapterFeatureConfigurationDoneRequest) |
361 | .Case(S: "supportsDataBreakpointBytes" , |
362 | Value: eAdapterFeatureDataBreakpointBytes) |
363 | .Case(S: "supportsDataBreakpoints" , Value: eAdapterFeatureDataBreakpoints) |
364 | .Case(S: "supportsDelayedStackTraceLoading" , |
365 | Value: eAdapterFeatureDelayedStackTraceLoading) |
366 | .Case(S: "supportsDisassembleRequest" , Value: eAdapterFeatureDisassembleRequest) |
367 | .Case(S: "supportsEvaluateForHovers" , Value: eAdapterFeatureEvaluateForHovers) |
368 | .Case(S: "supportsExceptionFilterOptions" , |
369 | Value: eAdapterFeatureExceptionFilterOptions) |
370 | .Case(S: "supportsExceptionInfoRequest" , |
371 | Value: eAdapterFeatureExceptionInfoRequest) |
372 | .Case(S: "supportsExceptionOptions" , Value: eAdapterFeatureExceptionOptions) |
373 | .Case(S: "supportsFunctionBreakpoints" , |
374 | Value: eAdapterFeatureFunctionBreakpoints) |
375 | .Case(S: "supportsGotoTargetsRequest" , Value: eAdapterFeatureGotoTargetsRequest) |
376 | .Case(S: "supportsHitConditionalBreakpoints" , |
377 | Value: eAdapterFeatureHitConditionalBreakpoints) |
378 | .Case(S: "supportsInstructionBreakpoints" , |
379 | Value: eAdapterFeatureInstructionBreakpoints) |
380 | .Case(S: "supportsLoadedSourcesRequest" , |
381 | Value: eAdapterFeatureLoadedSourcesRequest) |
382 | .Case(S: "supportsLogPoints" , Value: eAdapterFeatureLogPoints) |
383 | .Case(S: "supportsModulesRequest" , Value: eAdapterFeatureModulesRequest) |
384 | .Case(S: "supportsReadMemoryRequest" , Value: eAdapterFeatureReadMemoryRequest) |
385 | .Case(S: "supportsRestartFrame" , Value: eAdapterFeatureRestartFrame) |
386 | .Case(S: "supportsRestartRequest" , Value: eAdapterFeatureRestartRequest) |
387 | .Case(S: "supportsSetExpression" , Value: eAdapterFeatureSetExpression) |
388 | .Case(S: "supportsSetVariable" , Value: eAdapterFeatureSetVariable) |
389 | .Case(S: "supportsSingleThreadExecutionRequests" , |
390 | Value: eAdapterFeatureSingleThreadExecutionRequests) |
391 | .Case(S: "supportsStepBack" , Value: eAdapterFeatureStepBack) |
392 | .Case(S: "supportsStepInTargetsRequest" , |
393 | Value: eAdapterFeatureStepInTargetsRequest) |
394 | .Case(S: "supportsSteppingGranularity" , |
395 | Value: eAdapterFeatureSteppingGranularity) |
396 | .Case(S: "supportsTerminateRequest" , Value: eAdapterFeatureTerminateRequest) |
397 | .Case(S: "supportsTerminateThreadsRequest" , |
398 | Value: eAdapterFeatureTerminateThreadsRequest) |
399 | .Case(S: "supportSuspendDebuggee" , Value: eAdapterFeatureSuspendDebuggee) |
400 | .Case(S: "supportsValueFormattingOptions" , |
401 | Value: eAdapterFeatureValueFormattingOptions) |
402 | .Case(S: "supportsWriteMemoryRequest" , Value: eAdapterFeatureWriteMemoryRequest) |
403 | .Case(S: "supportTerminateDebuggee" , Value: eAdapterFeatureTerminateDebuggee) |
404 | .Default(Value: std::nullopt); |
405 | |
406 | if (!parsedFeature) { |
407 | P.report(Message: "unexpected value for AdapterFeature" ); |
408 | return false; |
409 | } |
410 | |
411 | feature = *parsedFeature; |
412 | return true; |
413 | } |
414 | |
415 | json::Value toJSON(const Capabilities &C) { |
416 | json::Object result; |
417 | |
418 | for (const auto &feature : C.supportedFeatures) |
419 | result.insert(E: {.K: ToString(feature), .V: true}); |
420 | |
421 | if (C.exceptionBreakpointFilters && !C.exceptionBreakpointFilters->empty()) |
422 | result.insert( |
423 | E: {.K: "exceptionBreakpointFilters" , .V: *C.exceptionBreakpointFilters}); |
424 | if (C.completionTriggerCharacters && !C.completionTriggerCharacters->empty()) |
425 | result.insert( |
426 | E: {.K: "completionTriggerCharacters" , .V: *C.completionTriggerCharacters}); |
427 | if (C.additionalModuleColumns && !C.additionalModuleColumns->empty()) |
428 | result.insert(E: {.K: "additionalModuleColumns" , .V: *C.additionalModuleColumns}); |
429 | if (C.supportedChecksumAlgorithms && !C.supportedChecksumAlgorithms->empty()) |
430 | result.insert( |
431 | E: {.K: "supportedChecksumAlgorithms" , .V: *C.supportedChecksumAlgorithms}); |
432 | if (C.breakpointModes && !C.breakpointModes->empty()) |
433 | result.insert(E: {.K: "breakpointModes" , .V: *C.breakpointModes}); |
434 | |
435 | // lldb-dap extensions |
436 | if (C.lldbExtVersion && !C.lldbExtVersion->empty()) |
437 | result.insert(E: {.K: "$__lldb_version" , .V: *C.lldbExtVersion}); |
438 | |
439 | return result; |
440 | } |
441 | |
442 | bool fromJSON(const json::Value &Params, Scope::PresentationHint &PH, |
443 | json::Path P) { |
444 | auto rawHint = Params.getAsString(); |
445 | if (!rawHint) { |
446 | P.report(Message: "expected a string" ); |
447 | return false; |
448 | } |
449 | const std::optional<Scope::PresentationHint> hint = |
450 | StringSwitch<std::optional<Scope::PresentationHint>>(*rawHint) |
451 | .Case(S: "arguments" , Value: Scope::eScopePresentationHintArguments) |
452 | .Case(S: "locals" , Value: Scope::eScopePresentationHintLocals) |
453 | .Case(S: "registers" , Value: Scope::eScopePresentationHintRegisters) |
454 | .Case(S: "returnValue" , Value: Scope::eScopePresentationHintReturnValue) |
455 | .Default(Value: std::nullopt); |
456 | if (!hint) { |
457 | P.report(Message: "unexpected value" ); |
458 | return false; |
459 | } |
460 | PH = *hint; |
461 | return true; |
462 | } |
463 | |
464 | bool fromJSON(const json::Value &Params, Scope &S, json::Path P) { |
465 | json::ObjectMapper O(Params, P); |
466 | return O && O.map(Prop: "name" , Out&: S.name) && |
467 | O.mapOptional(Prop: "presentationHint" , Out&: S.presentationHint) && |
468 | O.map(Prop: "variablesReference" , Out&: S.variablesReference) && |
469 | O.mapOptional(Prop: "namedVariables" , Out&: S.namedVariables) && |
470 | O.map(Prop: "indexedVariables" , Out&: S.indexedVariables) && |
471 | O.mapOptional(Prop: "source" , Out&: S.source) && O.map(Prop: "expensive" , Out&: S.expensive) && |
472 | O.mapOptional(Prop: "line" , Out&: S.line) && O.mapOptional(Prop: "column" , Out&: S.column) && |
473 | O.mapOptional(Prop: "endLine" , Out&: S.endLine) && |
474 | O.mapOptional(Prop: "endColumn" , Out&: S.endColumn); |
475 | } |
476 | |
477 | llvm::json::Value toJSON(const Scope &SC) { |
478 | llvm::json::Object result{{.K: "name" , .V: SC.name}, |
479 | {.K: "variablesReference" , .V: SC.variablesReference}, |
480 | {.K: "expensive" , .V: SC.expensive}}; |
481 | |
482 | if (SC.presentationHint.has_value()) { |
483 | llvm::StringRef presentationHint; |
484 | switch (*SC.presentationHint) { |
485 | case Scope::eScopePresentationHintArguments: |
486 | presentationHint = "arguments" ; |
487 | break; |
488 | case Scope::eScopePresentationHintLocals: |
489 | presentationHint = "locals" ; |
490 | break; |
491 | case Scope::eScopePresentationHintRegisters: |
492 | presentationHint = "registers" ; |
493 | break; |
494 | case Scope::eScopePresentationHintReturnValue: |
495 | presentationHint = "returnValue" ; |
496 | break; |
497 | } |
498 | |
499 | result.insert(E: {.K: "presentationHint" , .V: presentationHint}); |
500 | } |
501 | |
502 | if (SC.namedVariables.has_value()) |
503 | result.insert(E: {.K: "namedVariables" , .V: SC.namedVariables}); |
504 | |
505 | if (SC.indexedVariables.has_value()) |
506 | result.insert(E: {.K: "indexedVariables" , .V: SC.indexedVariables}); |
507 | |
508 | if (SC.source.has_value()) |
509 | result.insert(E: {.K: "source" , .V: SC.source}); |
510 | |
511 | if (SC.line.has_value()) |
512 | result.insert(E: {.K: "line" , .V: SC.line}); |
513 | |
514 | if (SC.column.has_value()) |
515 | result.insert(E: {.K: "column" , .V: SC.column}); |
516 | |
517 | if (SC.endLine.has_value()) |
518 | result.insert(E: {.K: "endLine" , .V: SC.endLine}); |
519 | |
520 | if (SC.endColumn.has_value()) |
521 | result.insert(E: {.K: "endColumn" , .V: SC.endColumn}); |
522 | |
523 | return result; |
524 | } |
525 | |
526 | bool fromJSON(const llvm::json::Value &Params, Capabilities &C, |
527 | llvm::json::Path P) { |
528 | auto *Object = Params.getAsObject(); |
529 | if (!Object) { |
530 | P.report(Message: "expected an object" ); |
531 | return false; |
532 | } |
533 | // Check for the presence of supported features. |
534 | for (unsigned i = eAdapterFeatureFirst; i <= eAdapterFeatureLast; ++i) { |
535 | AdapterFeature feature = static_cast<AdapterFeature>(i); |
536 | if (Object->getBoolean(K: ToString(feature))) |
537 | C.supportedFeatures.insert(V: feature); |
538 | } |
539 | llvm::json::ObjectMapper O(Params, P); |
540 | return O && |
541 | O.mapOptional(Prop: "exceptionBreakpointFilters" , |
542 | Out&: C.exceptionBreakpointFilters) && |
543 | O.mapOptional(Prop: "completionTriggerCharacters" , |
544 | Out&: C.completionTriggerCharacters) && |
545 | O.mapOptional(Prop: "additionalModuleColumns" , Out&: C.additionalModuleColumns) && |
546 | O.mapOptional(Prop: "supportedChecksumAlgorithms" , |
547 | Out&: C.supportedChecksumAlgorithms) && |
548 | O.mapOptional(Prop: "breakpointModes" , Out&: C.breakpointModes) && |
549 | O.mapOptional(Prop: "$__lldb_version" , Out&: C.lldbExtVersion); |
550 | } |
551 | |
552 | bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG, |
553 | llvm::json::Path P) { |
554 | auto raw_granularity = Params.getAsString(); |
555 | if (!raw_granularity) { |
556 | P.report(Message: "expected a string" ); |
557 | return false; |
558 | } |
559 | std::optional<SteppingGranularity> granularity = |
560 | StringSwitch<std::optional<SteppingGranularity>>(*raw_granularity) |
561 | .Case(S: "statement" , Value: eSteppingGranularityStatement) |
562 | .Case(S: "line" , Value: eSteppingGranularityLine) |
563 | .Case(S: "instruction" , Value: eSteppingGranularityInstruction) |
564 | .Default(Value: std::nullopt); |
565 | if (!granularity) { |
566 | P.report(Message: "unexpected value" ); |
567 | return false; |
568 | } |
569 | SG = *granularity; |
570 | return true; |
571 | } |
572 | |
573 | llvm::json::Value toJSON(const SteppingGranularity &SG) { |
574 | switch (SG) { |
575 | case eSteppingGranularityStatement: |
576 | return "statement" ; |
577 | case eSteppingGranularityLine: |
578 | return "line" ; |
579 | case eSteppingGranularityInstruction: |
580 | return "instruction" ; |
581 | } |
582 | llvm_unreachable("unhandled stepping granularity." ); |
583 | } |
584 | |
585 | bool fromJSON(const json::Value &Params, Thread &T, json::Path P) { |
586 | json::ObjectMapper O(Params, P); |
587 | return O && O.map(Prop: "id" , Out&: T.id) && O.map(Prop: "name" , Out&: T.name); |
588 | } |
589 | |
590 | json::Value toJSON(const Thread &T) { |
591 | return json::Object{{.K: "id" , .V: T.id}, {.K: "name" , .V: T.name}}; |
592 | } |
593 | |
594 | bool fromJSON(const llvm::json::Value &Params, ValueFormat &VF, |
595 | llvm::json::Path P) { |
596 | json::ObjectMapper O(Params, P); |
597 | return O && O.mapOptional(Prop: "hex" , Out&: VF.hex); |
598 | } |
599 | |
600 | json::Value toJSON(const BreakpointLocation &B) { |
601 | json::Object result; |
602 | |
603 | result.insert(E: {.K: "line" , .V: B.line}); |
604 | if (B.column) |
605 | result.insert(E: {.K: "column" , .V: *B.column}); |
606 | if (B.endLine) |
607 | result.insert(E: {.K: "endLine" , .V: *B.endLine}); |
608 | if (B.endColumn) |
609 | result.insert(E: {.K: "endColumn" , .V: *B.endColumn}); |
610 | |
611 | return result; |
612 | } |
613 | |
614 | llvm::json::Value toJSON(const BreakpointReason &BR) { |
615 | switch (BR) { |
616 | case BreakpointReason::eBreakpointReasonPending: |
617 | return "pending" ; |
618 | case BreakpointReason::eBreakpointReasonFailed: |
619 | return "failed" ; |
620 | } |
621 | llvm_unreachable("unhandled breakpoint reason." ); |
622 | } |
623 | |
624 | bool fromJSON(const llvm::json::Value &Params, BreakpointReason &BR, |
625 | llvm::json::Path P) { |
626 | auto rawReason = Params.getAsString(); |
627 | if (!rawReason) { |
628 | P.report(Message: "expected a string" ); |
629 | return false; |
630 | } |
631 | std::optional<BreakpointReason> reason = |
632 | llvm::StringSwitch<std::optional<BreakpointReason>>(*rawReason) |
633 | .Case(S: "pending" , Value: BreakpointReason::eBreakpointReasonPending) |
634 | .Case(S: "failed" , Value: BreakpointReason::eBreakpointReasonFailed) |
635 | .Default(Value: std::nullopt); |
636 | if (!reason) { |
637 | P.report(Message: "unexpected value, expected 'pending' or 'failed'" ); |
638 | return false; |
639 | } |
640 | BR = *reason; |
641 | return true; |
642 | } |
643 | |
644 | json::Value toJSON(const Breakpoint &BP) { |
645 | json::Object result{{.K: "verified" , .V: BP.verified}}; |
646 | |
647 | if (BP.id) |
648 | result.insert(E: {.K: "id" , .V: *BP.id}); |
649 | if (BP.message) |
650 | result.insert(E: {.K: "message" , .V: *BP.message}); |
651 | if (BP.source) |
652 | result.insert(E: {.K: "source" , .V: *BP.source}); |
653 | if (BP.line) |
654 | result.insert(E: {.K: "line" , .V: *BP.line}); |
655 | if (BP.column) |
656 | result.insert(E: {.K: "column" , .V: *BP.column}); |
657 | if (BP.endLine) |
658 | result.insert(E: {.K: "endLine" , .V: *BP.endLine}); |
659 | if (BP.endColumn) |
660 | result.insert(E: {.K: "endColumn" , .V: *BP.endColumn}); |
661 | if (BP.instructionReference) |
662 | result.insert(E: {.K: "instructionReference" , .V: *BP.instructionReference}); |
663 | if (BP.offset) |
664 | result.insert(E: {.K: "offset" , .V: *BP.offset}); |
665 | if (BP.reason) { |
666 | result.insert(E: {.K: "reason" , .V: *BP.reason}); |
667 | } |
668 | |
669 | return result; |
670 | } |
671 | |
672 | bool fromJSON(const llvm::json::Value &Params, Breakpoint &BP, |
673 | llvm::json::Path P) { |
674 | llvm::json::ObjectMapper O(Params, P); |
675 | return O && O.mapOptional(Prop: "id" , Out&: BP.id) && O.map(Prop: "verified" , Out&: BP.verified) && |
676 | O.mapOptional(Prop: "message" , Out&: BP.message) && |
677 | O.mapOptional(Prop: "source" , Out&: BP.source) && O.mapOptional(Prop: "line" , Out&: BP.line) && |
678 | O.mapOptional(Prop: "column" , Out&: BP.column) && |
679 | O.mapOptional(Prop: "endLine" , Out&: BP.endLine) && |
680 | O.mapOptional(Prop: "endColumn" , Out&: BP.endColumn) && |
681 | O.mapOptional(Prop: "instructionReference" , Out&: BP.instructionReference) && |
682 | O.mapOptional(Prop: "offset" , Out&: BP.offset) && |
683 | O.mapOptional(Prop: "reason" , Out&: BP.reason); |
684 | } |
685 | |
686 | bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB, |
687 | llvm::json::Path P) { |
688 | llvm::json::ObjectMapper O(Params, P); |
689 | return O && O.map(Prop: "line" , Out&: SB.line) && O.mapOptional(Prop: "column" , Out&: SB.column) && |
690 | O.mapOptional(Prop: "condition" , Out&: SB.condition) && |
691 | O.mapOptional(Prop: "hitCondition" , Out&: SB.hitCondition) && |
692 | O.mapOptional(Prop: "logMessage" , Out&: SB.logMessage) && |
693 | O.mapOptional(Prop: "mode" , Out&: SB.mode); |
694 | } |
695 | |
696 | llvm::json::Value toJSON(const SourceBreakpoint &SB) { |
697 | llvm::json::Object result{{.K: "line" , .V: SB.line}}; |
698 | |
699 | if (SB.column) |
700 | result.insert(E: {.K: "column" , .V: *SB.column}); |
701 | if (SB.condition) |
702 | result.insert(E: {.K: "condition" , .V: *SB.condition}); |
703 | if (SB.hitCondition) |
704 | result.insert(E: {.K: "hitCondition" , .V: *SB.hitCondition}); |
705 | if (SB.logMessage) |
706 | result.insert(E: {.K: "logMessage" , .V: *SB.logMessage}); |
707 | if (SB.mode) |
708 | result.insert(E: {.K: "mode" , .V: *SB.mode}); |
709 | |
710 | return result; |
711 | } |
712 | |
713 | bool fromJSON(const llvm::json::Value &Params, FunctionBreakpoint &FB, |
714 | llvm::json::Path P) { |
715 | llvm::json::ObjectMapper O(Params, P); |
716 | return O && O.map(Prop: "name" , Out&: FB.name) && |
717 | O.mapOptional(Prop: "condition" , Out&: FB.condition) && |
718 | O.mapOptional(Prop: "hitCondition" , Out&: FB.hitCondition); |
719 | } |
720 | |
721 | llvm::json::Value toJSON(const FunctionBreakpoint &FB) { |
722 | llvm::json::Object result{{.K: "name" , .V: FB.name}}; |
723 | |
724 | if (FB.condition) |
725 | result.insert(E: {.K: "condition" , .V: *FB.condition}); |
726 | if (FB.hitCondition) |
727 | result.insert(E: {.K: "hitCondition" , .V: *FB.hitCondition}); |
728 | |
729 | return result; |
730 | } |
731 | |
732 | bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT, |
733 | llvm::json::Path P) { |
734 | auto rawAccessType = Params.getAsString(); |
735 | if (!rawAccessType) { |
736 | P.report(Message: "expected a string" ); |
737 | return false; |
738 | } |
739 | std::optional<DataBreakpointAccessType> accessType = |
740 | StringSwitch<std::optional<DataBreakpointAccessType>>(*rawAccessType) |
741 | .Case(S: "read" , Value: eDataBreakpointAccessTypeRead) |
742 | .Case(S: "write" , Value: eDataBreakpointAccessTypeWrite) |
743 | .Case(S: "readWrite" , Value: eDataBreakpointAccessTypeReadWrite) |
744 | .Default(Value: std::nullopt); |
745 | if (!accessType) { |
746 | P.report(Message: "unexpected value, expected 'read', 'write', or 'readWrite'" ); |
747 | return false; |
748 | } |
749 | DBAT = *accessType; |
750 | return true; |
751 | } |
752 | |
753 | llvm::json::Value toJSON(const DataBreakpointAccessType &DBAT) { |
754 | switch (DBAT) { |
755 | case eDataBreakpointAccessTypeRead: |
756 | return "read" ; |
757 | case eDataBreakpointAccessTypeWrite: |
758 | return "write" ; |
759 | case eDataBreakpointAccessTypeReadWrite: |
760 | return "readWrite" ; |
761 | } |
762 | llvm_unreachable("unhandled data breakpoint access type." ); |
763 | } |
764 | |
765 | bool fromJSON(const llvm::json::Value &Params, DataBreakpoint &DBI, |
766 | llvm::json::Path P) { |
767 | llvm::json::ObjectMapper O(Params, P); |
768 | return O && O.map(Prop: "dataId" , Out&: DBI.dataId) && |
769 | O.mapOptional(Prop: "accessType" , Out&: DBI.accessType) && |
770 | O.mapOptional(Prop: "condition" , Out&: DBI.condition) && |
771 | O.mapOptional(Prop: "hitCondition" , Out&: DBI.hitCondition); |
772 | } |
773 | |
774 | llvm::json::Value toJSON(const DataBreakpoint &DBI) { |
775 | llvm::json::Object result{{.K: "dataId" , .V: DBI.dataId}}; |
776 | |
777 | if (DBI.accessType) |
778 | result.insert(E: {.K: "accessType" , .V: *DBI.accessType}); |
779 | if (DBI.condition) |
780 | result.insert(E: {.K: "condition" , .V: *DBI.condition}); |
781 | if (DBI.hitCondition) |
782 | result.insert(E: {.K: "hitCondition" , .V: *DBI.hitCondition}); |
783 | |
784 | return result; |
785 | } |
786 | |
787 | bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB, |
788 | llvm::json::Path P) { |
789 | llvm::json::ObjectMapper O(Params, P); |
790 | return O && O.map(Prop: "instructionReference" , Out&: IB.instructionReference) && |
791 | O.mapOptional(Prop: "offset" , Out&: IB.offset) && |
792 | O.mapOptional(Prop: "condition" , Out&: IB.condition) && |
793 | O.mapOptional(Prop: "hitCondition" , Out&: IB.hitCondition) && |
794 | O.mapOptional(Prop: "mode" , Out&: IB.mode); |
795 | } |
796 | |
797 | bool fromJSON(const llvm::json::Value &Params, |
798 | DisassembledInstruction::PresentationHint &PH, |
799 | llvm::json::Path P) { |
800 | auto rawHint = Params.getAsString(); |
801 | if (!rawHint) { |
802 | P.report(Message: "expected a string" ); |
803 | return false; |
804 | } |
805 | std::optional<DisassembledInstruction::PresentationHint> hint = |
806 | StringSwitch<std::optional<DisassembledInstruction::PresentationHint>>( |
807 | *rawHint) |
808 | .Case(S: "normal" , Value: DisassembledInstruction:: |
809 | eDisassembledInstructionPresentationHintNormal) |
810 | .Case(S: "invalid" , Value: DisassembledInstruction:: |
811 | eDisassembledInstructionPresentationHintInvalid) |
812 | .Default(Value: std::nullopt); |
813 | if (!hint) { |
814 | P.report(Message: "unexpected value" ); |
815 | return false; |
816 | } |
817 | PH = *hint; |
818 | return true; |
819 | } |
820 | |
821 | llvm::json::Value toJSON(const DisassembledInstruction::PresentationHint &PH) { |
822 | switch (PH) { |
823 | case DisassembledInstruction::eDisassembledInstructionPresentationHintNormal: |
824 | return "normal" ; |
825 | case DisassembledInstruction::eDisassembledInstructionPresentationHintInvalid: |
826 | return "invalid" ; |
827 | } |
828 | llvm_unreachable("unhandled presentation hint." ); |
829 | } |
830 | |
831 | bool fromJSON(const llvm::json::Value &Params, DisassembledInstruction &DI, |
832 | llvm::json::Path P) { |
833 | llvm::json::ObjectMapper O(Params, P); |
834 | std::string raw_address; |
835 | if (!O || !O.map(Prop: "address" , Out&: raw_address)) |
836 | return false; |
837 | |
838 | std::optional<lldb::addr_t> address = DecodeMemoryReference(memoryReference: raw_address); |
839 | if (!address) { |
840 | P.field(Field: "address" ).report(Message: "expected string encoded uint64_t" ); |
841 | return false; |
842 | } |
843 | |
844 | DI.address = *address; |
845 | |
846 | return O.map(Prop: "instruction" , Out&: DI.instruction) && |
847 | O.mapOptional(Prop: "instructionBytes" , Out&: DI.instructionBytes) && |
848 | O.mapOptional(Prop: "symbol" , Out&: DI.symbol) && |
849 | O.mapOptional(Prop: "location" , Out&: DI.location) && |
850 | O.mapOptional(Prop: "line" , Out&: DI.line) && O.mapOptional(Prop: "column" , Out&: DI.column) && |
851 | O.mapOptional(Prop: "endLine" , Out&: DI.endLine) && |
852 | O.mapOptional(Prop: "endColumn" , Out&: DI.endColumn) && |
853 | O.mapOptional(Prop: "presentationHint" , Out&: DI.presentationHint); |
854 | } |
855 | |
856 | llvm::json::Value toJSON(const DisassembledInstruction &DI) { |
857 | llvm::json::Object result{{.K: "instruction" , .V: DI.instruction}}; |
858 | if (DI.address == LLDB_INVALID_ADDRESS) { |
859 | // VS Code has explicit comparisons to the string "-1" in order to check for |
860 | // invalid instructions. See |
861 | // https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/debug/browser/disassemblyView.ts |
862 | result.insert(E: {.K: "address" , .V: "-1" }); |
863 | } else { |
864 | result.insert(E: {.K: "address" , .V: "0x" + llvm::utohexstr(X: DI.address)}); |
865 | } |
866 | |
867 | if (DI.instructionBytes) |
868 | result.insert(E: {.K: "instructionBytes" , .V: *DI.instructionBytes}); |
869 | if (DI.symbol) |
870 | result.insert(E: {.K: "symbol" , .V: *DI.symbol}); |
871 | if (DI.location) |
872 | result.insert(E: {.K: "location" , .V: *DI.location}); |
873 | if (DI.line) |
874 | result.insert(E: {.K: "line" , .V: *DI.line}); |
875 | if (DI.column) |
876 | result.insert(E: {.K: "column" , .V: *DI.column}); |
877 | if (DI.endLine) |
878 | result.insert(E: {.K: "endLine" , .V: *DI.endLine}); |
879 | if (DI.endColumn) |
880 | result.insert(E: {.K: "endColumn" , .V: *DI.endColumn}); |
881 | if (DI.presentationHint) |
882 | result.insert(E: {.K: "presentationHint" , .V: *DI.presentationHint}); |
883 | |
884 | return result; |
885 | } |
886 | |
887 | } // namespace lldb_dap::protocol |
888 | |