1//===-- CommandPlugins.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 "CommandPlugins.h"
10#include "Handler/ResponseHandler.h"
11#include "JSONUtils.h"
12#include "lldb/API/SBStream.h"
13
14using namespace lldb_dap;
15
16bool StartDebuggingCommand::DoExecute(lldb::SBDebugger debugger, char **command,
17 lldb::SBCommandReturnObject &result) {
18 // Command format like: `start-debugging <launch|attach> <configuration>`
19 if (!command) {
20 result.SetError("Invalid use of start-debugging, expected format "
21 "`start-debugging <launch|attach> <configuration>`.");
22 return false;
23 }
24
25 if (!command[0] || llvm::StringRef(command[0]).empty()) {
26 result.SetError("start-debugging request type missing.");
27 return false;
28 }
29
30 if (!command[1] || llvm::StringRef(command[1]).empty()) {
31 result.SetError("start-debugging debug configuration missing.");
32 return false;
33 }
34
35 llvm::StringRef request{command[0]};
36 std::string raw_configuration{command[1]};
37
38 llvm::Expected<llvm::json::Value> configuration =
39 llvm::json::parse(JSON: raw_configuration);
40
41 if (!configuration) {
42 llvm::Error err = configuration.takeError();
43 std::string msg = "Failed to parse json configuration: " +
44 llvm::toString(E: std::move(err)) + "\n\n" +
45 raw_configuration;
46 result.SetError(msg.c_str());
47 return false;
48 }
49
50 dap.SendReverseRequest<LogFailureResponseHandler>(
51 command: "startDebugging",
52 arguments: llvm::json::Object{{.K: "request", .V: request},
53 {.K: "configuration", .V: std::move(*configuration)}});
54
55 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
56
57 return true;
58}
59
60bool ReplModeCommand::DoExecute(lldb::SBDebugger debugger, char **command,
61 lldb::SBCommandReturnObject &result) {
62 // Command format like: `repl-mode <variable|command|auto>?`
63 // If a new mode is not specified report the current mode.
64 if (!command || llvm::StringRef(command[0]).empty()) {
65 std::string mode;
66 switch (dap.repl_mode) {
67 case ReplMode::Variable:
68 mode = "variable";
69 break;
70 case ReplMode::Command:
71 mode = "command";
72 break;
73 case ReplMode::Auto:
74 mode = "auto";
75 break;
76 }
77
78 result.Printf(format: "lldb-dap repl-mode %s.\n", mode.c_str());
79 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
80
81 return true;
82 }
83
84 llvm::StringRef new_mode{command[0]};
85
86 if (new_mode == "variable") {
87 dap.repl_mode = ReplMode::Variable;
88 } else if (new_mode == "command") {
89 dap.repl_mode = ReplMode::Command;
90 } else if (new_mode == "auto") {
91 dap.repl_mode = ReplMode::Auto;
92 } else {
93 lldb::SBStream error_message;
94 error_message.Printf(format: "Invalid repl-mode '%s'. Expected one of 'variable', "
95 "'command' or 'auto'.\n",
96 new_mode.data());
97 result.SetError(error_message.GetData());
98 return false;
99 }
100
101 result.Printf(format: "lldb-dap repl-mode %s set.\n", new_mode.data());
102 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
103 return true;
104}
105
106/// Sends a DAP event with an optional body.
107///
108/// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
109bool SendEventCommand::DoExecute(lldb::SBDebugger debugger, char **command,
110 lldb::SBCommandReturnObject &result) {
111 // Command format like: `send-event <name> <body>?`
112 if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
113 result.SetError("Not enough arguments found, expected format "
114 "`lldb-dap send-event <name> <body>?`.");
115 return false;
116 }
117
118 llvm::StringRef name{command[0]};
119 // Events that are stateful and should be handled by lldb-dap internally.
120 const std::array internal_events{"breakpoint", "capabilities", "continued",
121 "exited", "initialize", "loadedSource",
122 "module", "process", "stopped",
123 "terminated", "thread"};
124 if (llvm::is_contained(Range: internal_events, Element: name)) {
125 std::string msg =
126 llvm::formatv(Fmt: "Invalid use of lldb-dap send-event, event \"{0}\" "
127 "should be handled by lldb-dap internally.",
128 Vals&: name)
129 .str();
130 result.SetError(msg.c_str());
131 return false;
132 }
133
134 llvm::json::Object event(CreateEventObject(event_name: name));
135
136 if (command[1] && !llvm::StringRef(command[1]).empty()) {
137 // See if we have unused arguments.
138 if (command[2]) {
139 result.SetError(
140 "Additional arguments found, expected `lldb-dap send-event "
141 "<name> <body>?`.");
142 return false;
143 }
144
145 llvm::StringRef raw_body{command[1]};
146
147 llvm::Expected<llvm::json::Value> body = llvm::json::parse(JSON: raw_body);
148
149 if (!body) {
150 llvm::Error err = body.takeError();
151 std::string msg = "Failed to parse custom event body: " +
152 llvm::toString(E: std::move(err));
153 result.SetError(msg.c_str());
154 return false;
155 }
156
157 event.try_emplace(K: "body", Args: std::move(*body));
158 }
159
160 dap.SendJSON(json: llvm::json::Value(std::move(event)));
161 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
162 return true;
163}
164

source code of lldb/tools/lldb-dap/CommandPlugins.cpp