blob: 9e9cfb13d77b8c95617fc69c843782830f824189 [file] [log] [blame]
//===-- Request.h ---------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
#define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H
#include "DAP.h"
#include "DAPError.h"
#include "DAPLog.h"
#include "Protocol/ProtocolBase.h"
#include "Protocol/ProtocolRequests.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBError.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include <optional>
#include <type_traits>
template <typename T> struct is_optional : std::false_type {};
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
template <typename T>
inline constexpr bool is_optional_v = is_optional<T>::value;
namespace lldb_dap {
struct DAP;
/// Base class for request handlers. Do not extend this directly: Extend
/// the RequestHandler template subclass instead.
class BaseRequestHandler {
public:
BaseRequestHandler(DAP &dap) : dap(dap) {}
/// BaseRequestHandler are not copyable.
/// @{
BaseRequestHandler(const BaseRequestHandler &) = delete;
BaseRequestHandler &operator=(const BaseRequestHandler &) = delete;
/// @}
virtual ~BaseRequestHandler() = default;
void Run(const protocol::Request &);
virtual void operator()(const protocol::Request &request) const = 0;
using FeatureSet = llvm::SmallDenseSet<AdapterFeature, 1>;
virtual FeatureSet GetSupportedFeatures() const { return {}; }
protected:
/// Helpers used by multiple request handlers.
/// FIXME: Move these into the DAP class?
/// @{
/// Both attach and launch take a either a sourcePath or sourceMap
/// argument (or neither), from which we need to set the target.source-map.
void SetSourceMapFromArguments(const llvm::json::Object &arguments) const;
/// Prints a welcome message on the editor if the preprocessor variable
/// LLDB_DAP_WELCOME_MESSAGE is defined.
void PrintWelcomeMessage() const;
// Takes a LaunchRequest object and launches the process, also handling
// runInTerminal if applicable. It doesn't do any of the additional
// initialization and bookkeeping stuff that is needed for `request_launch`.
// This way we can reuse the process launching logic for RestartRequest too.
llvm::Error
LaunchProcess(const protocol::LaunchRequestArguments &request) const;
// Check if the step-granularity is `instruction`.
bool HasInstructionGranularity(const llvm::json::Object &request) const;
/// @}
DAP &dap;
};
/// FIXME: Migrate callers to typed RequestHandler for improved type handling.
class LegacyRequestHandler : public BaseRequestHandler {
using BaseRequestHandler::BaseRequestHandler;
virtual void operator()(const llvm::json::Object &request) const = 0;
void operator()(const protocol::Request &request) const override {
auto req = toJSON(request);
(*this)(*req.getAsObject());
}
};
/// Base class for handling DAP requests. Handlers should declare their
/// arguments and response body types like:
///
/// class MyRequestHandler : public RequestHandler<Arguments, Response> {
/// ....
/// };
template <typename Args, typename Resp>
class RequestHandler : public BaseRequestHandler {
using BaseRequestHandler::BaseRequestHandler;
void operator()(const protocol::Request &request) const override {
protocol::Response response;
response.request_seq = request.seq;
response.command = request.command;
if (!is_optional_v<Args> && !request.arguments) {
DAP_LOG(dap.log,
"({0}) malformed request {1}, expected arguments but got none",
dap.transport.GetClientName(), request.command);
response.success = false;
response.message = llvm::formatv("arguments required for command '{0}' "
"but none received",
request.command)
.str();
dap.Send(response);
return;
}
Args arguments;
llvm::json::Path::Root root("arguments");
if (request.arguments && !fromJSON(*request.arguments, arguments, root)) {
std::string parse_failure;
llvm::raw_string_ostream OS(parse_failure);
OS << "invalid arguments for request '" << request.command
<< "': " << llvm::toString(root.getError()) << "\n";
root.printErrorContext(*request.arguments, OS);
response.success = false;
response.body = ToResponse(llvm::make_error<DAPError>(parse_failure));
dap.Send(response);
return;
}
if constexpr (std::is_same_v<Resp, llvm::Error>) {
if (llvm::Error err = Run(arguments)) {
response.success = false;
response.body = ToResponse(std::move(err));
} else {
response.success = true;
}
} else {
Resp body = Run(arguments);
if (llvm::Error err = body.takeError()) {
response.success = false;
response.body = ToResponse(std::move(err));
} else {
response.success = true;
response.body = std::move(*body);
}
}
// Mark the request as 'cancelled' if the debugger was interrupted while
// evaluating this handler.
if (dap.debugger.InterruptRequested()) {
dap.debugger.CancelInterruptRequest();
response.success = false;
response.message = protocol::eResponseMessageCancelled;
response.body = std::nullopt;
}
dap.Send(response);
PostRun();
};
virtual Resp Run(const Args &) const = 0;
/// A hook for a request handler to run additional operations after the
/// request response is sent but before the next request handler.
virtual void PostRun() const {};
protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
protocol::ErrorMessage error_message;
// Default to showing the user errors unless otherwise specified by a
// DAPError.
error_message.showUser = true;
error_message.sendTelemetry = false;
if (llvm::Error unhandled = llvm::handleErrors(
std::move(err), [&](const DAPError &E) -> llvm::Error {
error_message.format = E.getMessage();
error_message.showUser = E.getShowUser();
error_message.id = E.convertToErrorCode().value();
error_message.url = E.getURL();
error_message.urlLabel = E.getURLLabel();
return llvm::Error::success();
})) {
error_message.format = llvm::toString(std::move(unhandled));
}
protocol::ErrorResponseBody body;
body.error = error_message;
return body;
}
};
class AttachRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "attach"; }
void operator()(const llvm::json::Object &request) const override;
};
class BreakpointLocationsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "breakpointLocations"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureBreakpointLocationsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class CompletionsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "completions"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureCompletionsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class ContinueRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "continue"; }
void operator()(const llvm::json::Object &request) const override;
};
class ConfigurationDoneRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "configurationDone"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureConfigurationDoneRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class DisconnectRequestHandler
: public RequestHandler<std::optional<protocol::DisconnectArguments>,
protocol::DisconnectResponse> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "disconnect"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureTerminateDebuggee};
}
llvm::Error
Run(const std::optional<protocol::DisconnectArguments> &args) const override;
};
class EvaluateRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "evaluate"; }
void operator()(const llvm::json::Object &request) const override;
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureEvaluateForHovers};
}
};
class ExceptionInfoRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "exceptionInfo"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureExceptionInfoRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class InitializeRequestHandler
: public RequestHandler<protocol::InitializeRequestArguments,
llvm::Expected<protocol::InitializeResponseBody>> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "initialize"; }
llvm::Expected<protocol::InitializeResponseBody>
Run(const protocol::InitializeRequestArguments &args) const override;
void PostRun() const override;
};
class LaunchRequestHandler
: public RequestHandler<protocol::LaunchRequestArguments,
protocol::LaunchResponseBody> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "launch"; }
llvm::Error
Run(const protocol::LaunchRequestArguments &arguments) const override;
void PostRun() const override;
};
class RestartRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "restart"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureRestartRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class NextRequestHandler
: public RequestHandler<protocol::NextArguments, protocol::NextResponse> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "next"; }
llvm::Error Run(const protocol::NextArguments &args) const override;
};
class StepInRequestHandler : public RequestHandler<protocol::StepInArguments,
protocol::StepInResponse> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "stepIn"; }
llvm::Error Run(const protocol::StepInArguments &args) const override;
};
class StepInTargetsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "stepInTargets"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureStepInTargetsRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class StepOutRequestHandler : public RequestHandler<protocol::StepOutArguments,
protocol::StepOutResponse> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "stepOut"; }
llvm::Error Run(const protocol::StepOutArguments &args) const override;
};
class SetBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setBreakpoints"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureConditionalBreakpoints,
protocol::eAdapterFeatureHitConditionalBreakpoints};
}
void operator()(const llvm::json::Object &request) const override;
};
class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setExceptionBreakpoints"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureExceptionOptions};
}
void operator()(const llvm::json::Object &request) const override;
};
class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureFunctionBreakpoints};
}
void operator()(const llvm::json::Object &request) const override;
};
class DataBreakpointInfoRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
void operator()(const llvm::json::Object &request) const override;
};
class SetDataBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; }
void operator()(const llvm::json::Object &request) const override;
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureDataBreakpoints};
}
};
class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() {
return "setInstructionBreakpoints";
}
void operator()(const llvm::json::Object &request) const override;
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureInstructionBreakpoints};
}
};
class CompileUnitsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "compileUnits"; }
void operator()(const llvm::json::Object &request) const override;
};
class ModulesRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "modules"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureModulesRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class PauseRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "pause"; }
void operator()(const llvm::json::Object &request) const override;
};
class ScopesRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "scopes"; }
void operator()(const llvm::json::Object &request) const override;
};
class SetVariableRequestHandler final
: public RequestHandler<protocol::SetVariableArguments,
llvm::Expected<protocol::SetVariableResponseBody>> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "setVariable"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureSetVariable};
}
llvm::Expected<protocol::SetVariableResponseBody>
Run(const protocol::SetVariableArguments &args) const override;
};
class SourceRequestHandler final
: public RequestHandler<protocol::SourceArguments,
llvm::Expected<protocol::SourceResponseBody>> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "source"; }
llvm::Expected<protocol::SourceResponseBody>
Run(const protocol::SourceArguments &args) const override;
};
class StackTraceRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "stackTrace"; }
void operator()(const llvm::json::Object &request) const override;
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureDelayedStackTraceLoading};
}
};
class ThreadsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "threads"; }
void operator()(const llvm::json::Object &request) const override;
};
class VariablesRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "variables"; }
void operator()(const llvm::json::Object &request) const override;
};
class LocationsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "locations"; }
void operator()(const llvm::json::Object &request) const override;
};
class DisassembleRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "disassemble"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureDisassembleRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class ReadMemoryRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "readMemory"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureReadMemoryRequest};
}
void operator()(const llvm::json::Object &request) const override;
};
class CancelRequestHandler
: public RequestHandler<protocol::CancelArguments,
protocol::CancelResponseBody> {
public:
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "cancel"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureCancelRequest};
}
llvm::Error Run(const protocol::CancelArguments &args) const override;
};
/// A request used in testing to get the details on all breakpoints that are
/// currently set in the target. This helps us to test "setBreakpoints" and
/// "setFunctionBreakpoints" requests to verify we have the correct set of
/// breakpoints currently set in LLDB.
class TestGetTargetBreakpointsRequestHandler : public LegacyRequestHandler {
public:
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() {
return "_testGetTargetBreakpoints";
}
void operator()(const llvm::json::Object &request) const override;
};
} // namespace lldb_dap
#endif