[lldb][lldb-dap] Migrate ScopesRequest to structured types (#138116)
Migrate ScopesRequest To use the Protocol Types
This commit is contained in:
parent
156985eb88
commit
4ba8f4e213
@ -559,17 +559,6 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
|
||||
return GetLLDBFrame(frame_id);
|
||||
}
|
||||
|
||||
llvm::json::Value DAP::CreateTopLevelScopes() {
|
||||
llvm::json::Array scopes;
|
||||
scopes.emplace_back(
|
||||
CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
|
||||
scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
|
||||
variables.globals.GetSize(), false));
|
||||
scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
|
||||
variables.registers.GetSize(), false));
|
||||
return llvm::json::Value(std::move(scopes));
|
||||
}
|
||||
|
||||
ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
|
||||
bool partial_expression) {
|
||||
// Check for the escape hatch prefix.
|
||||
|
@ -283,10 +283,10 @@ struct DAP {
|
||||
lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
|
||||
|
||||
lldb::SBFrame GetLLDBFrame(uint64_t frame_id);
|
||||
/// TODO: remove this function when we finish migrating to the
|
||||
/// new protocol types.
|
||||
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
|
||||
|
||||
llvm::json::Value CreateTopLevelScopes();
|
||||
|
||||
void PopulateExceptionBreakpoints();
|
||||
|
||||
/// Attempt to determine if an expression is a variable expression or
|
||||
|
@ -452,11 +452,15 @@ public:
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
};
|
||||
|
||||
class ScopesRequestHandler : public LegacyRequestHandler {
|
||||
class ScopesRequestHandler final
|
||||
: public RequestHandler<protocol::ScopesArguments,
|
||||
llvm::Expected<protocol::ScopesResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() { return "scopes"; }
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
|
||||
llvm::Expected<protocol::ScopesResponseBody>
|
||||
Run(const protocol::ScopesArguments &args) const override;
|
||||
};
|
||||
|
||||
class SetVariableRequestHandler final
|
||||
|
@ -7,69 +7,56 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "RequestHandler.h"
|
||||
|
||||
using namespace lldb_dap::protocol;
|
||||
namespace lldb_dap {
|
||||
|
||||
// "ScopesRequest": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
||||
// "type": "object",
|
||||
// "description": "Scopes request; value of command field is 'scopes'. The
|
||||
// request returns the variable scopes for a given stackframe ID.",
|
||||
// "properties": {
|
||||
// "command": {
|
||||
// "type": "string",
|
||||
// "enum": [ "scopes" ]
|
||||
// },
|
||||
// "arguments": {
|
||||
// "$ref": "#/definitions/ScopesArguments"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }]
|
||||
// },
|
||||
// "ScopesArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for 'scopes' request.",
|
||||
// "properties": {
|
||||
// "frameId": {
|
||||
// "type": "integer",
|
||||
// "description": "Retrieve the scopes for this stackframe."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "frameId" ]
|
||||
// },
|
||||
// "ScopesResponse": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
||||
// "type": "object",
|
||||
// "description": "Response to 'scopes' request.",
|
||||
// "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "scopes": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/Scope"
|
||||
// },
|
||||
// "description": "The scopes of the stackframe. If the array has
|
||||
// length zero, there are no scopes available."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "scopes" ]
|
||||
// }
|
||||
// },
|
||||
// "required": [ "body" ]
|
||||
// }]
|
||||
// }
|
||||
void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
FillResponse(request, response);
|
||||
llvm::json::Object body;
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
|
||||
/// Creates a `protocol::Scope` struct.
|
||||
///
|
||||
///
|
||||
/// \param[in] name
|
||||
/// The value to place into the "name" key
|
||||
///
|
||||
/// \param[in] variablesReference
|
||||
/// The value to place into the "variablesReference" key
|
||||
///
|
||||
/// \param[in] namedVariables
|
||||
/// The value to place into the "namedVariables" key
|
||||
///
|
||||
/// \param[in] expensive
|
||||
/// The value to place into the "expensive" key
|
||||
///
|
||||
/// \return
|
||||
/// A `protocol::Scope`
|
||||
static Scope CreateScope(const llvm::StringRef name, int64_t variablesReference,
|
||||
int64_t namedVariables, bool expensive) {
|
||||
Scope scope;
|
||||
scope.name = name;
|
||||
|
||||
// TODO: Support "arguments" and "return value" scope.
|
||||
// At the moment lldb-dap includes the arguments and return_value into the
|
||||
// "locals" scope.
|
||||
// vscode only expands the first non-expensive scope, this causes friction
|
||||
// if we add the arguments above the local scope as the locals scope will not
|
||||
// be expanded if we enter a function with arguments. It becomes more
|
||||
// annoying when the scope has arguments, return_value and locals.
|
||||
if (variablesReference == VARREF_LOCALS)
|
||||
scope.presentationHint = Scope::eScopePresentationHintLocals;
|
||||
else if (variablesReference == VARREF_REGS)
|
||||
scope.presentationHint = Scope::eScopePresentationHintRegisters;
|
||||
|
||||
scope.variablesReference = variablesReference;
|
||||
scope.namedVariables = namedVariables;
|
||||
scope.expensive = expensive;
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
llvm::Expected<ScopesResponseBody>
|
||||
ScopesRequestHandler::Run(const ScopesArguments &args) const {
|
||||
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
|
||||
|
||||
// As the user selects different stack frames in the GUI, a "scopes" request
|
||||
// will be sent to the DAP. This is the only way we know that the user has
|
||||
// selected a frame in a thread. There are no other notifications that are
|
||||
@ -78,9 +65,9 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
// are sent, this allows users to type commands in the debugger console
|
||||
// with a backtick character to run lldb commands and these lldb commands
|
||||
// will now have the right context selected as they are run. If the user
|
||||
// types "`bt" into the debugger console and we had another thread selected
|
||||
// types "`bt" into the debugger console, and we had another thread selected
|
||||
// in the LLDB library, we would show the wrong thing to the user. If the
|
||||
// users switches threads with a lldb command like "`thread select 14", the
|
||||
// users switch threads with a lldb command like "`thread select 14", the
|
||||
// GUI will not update as there are no "event" notification packets that
|
||||
// allow us to change the currently selected thread or frame in the GUI that
|
||||
// I am aware of.
|
||||
@ -88,7 +75,6 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
|
||||
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
|
||||
}
|
||||
|
||||
dap.variables.locals = frame.GetVariables(/*arguments=*/true,
|
||||
/*locals=*/true,
|
||||
/*statics=*/false,
|
||||
@ -98,9 +84,15 @@ void ScopesRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
/*statics=*/true,
|
||||
/*in_scope_only=*/true);
|
||||
dap.variables.registers = frame.GetRegisters();
|
||||
body.try_emplace("scopes", dap.CreateTopLevelScopes());
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
|
||||
std::vector scopes = {CreateScope("Locals", VARREF_LOCALS,
|
||||
dap.variables.locals.GetSize(), false),
|
||||
CreateScope("Globals", VARREF_GLOBALS,
|
||||
dap.variables.globals.GetSize(), false),
|
||||
CreateScope("Registers", VARREF_REGS,
|
||||
dap.variables.registers.GetSize(), false)};
|
||||
|
||||
return ScopesResponseBody{std::move(scopes)};
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -238,27 +238,6 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
|
||||
protocol::ExceptionBreakpointsFilter
|
||||
CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp);
|
||||
|
||||
/// Create a "Scope" JSON object as described in the debug adapter definition.
|
||||
///
|
||||
/// \param[in] name
|
||||
/// The value to place into the "name" key
|
||||
//
|
||||
/// \param[in] variablesReference
|
||||
/// The value to place into the "variablesReference" key
|
||||
//
|
||||
/// \param[in] namedVariables
|
||||
/// The value to place into the "namedVariables" key
|
||||
//
|
||||
/// \param[in] expensive
|
||||
/// The value to place into the "expensive" key
|
||||
///
|
||||
/// \return
|
||||
/// A "Scope" JSON object with that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateScope(const llvm::StringRef name,
|
||||
int64_t variablesReference,
|
||||
int64_t namedVariables, bool expensive);
|
||||
|
||||
/// Create a "Source" JSON object as described in the debug adapter definition.
|
||||
///
|
||||
/// \param[in] file
|
||||
|
@ -335,6 +335,20 @@ llvm::json::Value toJSON(const SetVariableResponseBody &SVR) {
|
||||
|
||||
return llvm::json::Value(std::move(Body));
|
||||
}
|
||||
bool fromJSON(const llvm::json::Value &Params, ScopesArguments &SCA,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("frameId", SCA.frameId);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const ScopesResponseBody &SCR) {
|
||||
llvm::json::Array scopes;
|
||||
for (const Scope &scope : SCR.scopes) {
|
||||
scopes.emplace_back(toJSON(scope));
|
||||
}
|
||||
|
||||
return llvm::json::Object{{"scopes", std::move(scopes)}};
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
|
@ -439,6 +439,19 @@ struct SetVariableResponseBody {
|
||||
};
|
||||
llvm::json::Value toJSON(const SetVariableResponseBody &);
|
||||
|
||||
struct ScopesArguments {
|
||||
/// Retrieve the scopes for the stack frame identified by `frameId`. The
|
||||
/// `frameId` must have been obtained in the current suspended state. See
|
||||
/// 'Lifetime of Object References' in the Overview section for details.
|
||||
uint64_t frameId = LLDB_INVALID_FRAME_ID;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
|
||||
|
||||
struct ScopesResponseBody {
|
||||
std::vector<Scope> scopes;
|
||||
};
|
||||
llvm::json::Value toJSON(const ScopesResponseBody &);
|
||||
|
||||
/// Arguments for `source` request.
|
||||
struct SourceArguments {
|
||||
/// Specifies the source content to load. Either `source.path` or
|
||||
|
@ -16,17 +16,18 @@ using namespace llvm;
|
||||
|
||||
namespace lldb_dap::protocol {
|
||||
|
||||
bool fromJSON(const json::Value &Params, PresentationHint &PH, json::Path P) {
|
||||
bool fromJSON(const json::Value &Params, Source::PresentationHint &PH,
|
||||
json::Path P) {
|
||||
auto rawHint = Params.getAsString();
|
||||
if (!rawHint) {
|
||||
P.report("expected a string");
|
||||
return false;
|
||||
}
|
||||
std::optional<PresentationHint> hint =
|
||||
StringSwitch<std::optional<PresentationHint>>(*rawHint)
|
||||
.Case("normal", ePresentationHintNormal)
|
||||
.Case("emphasize", ePresentationHintEmphasize)
|
||||
.Case("deemphasize", ePresentationHintDeemphasize)
|
||||
std::optional<Source::PresentationHint> hint =
|
||||
StringSwitch<std::optional<Source::PresentationHint>>(*rawHint)
|
||||
.Case("normal", Source::eSourcePresentationHintNormal)
|
||||
.Case("emphasize", Source::eSourcePresentationHintEmphasize)
|
||||
.Case("deemphasize", Source::eSourcePresentationHintDeemphasize)
|
||||
.Default(std::nullopt);
|
||||
if (!hint) {
|
||||
P.report("unexpected value");
|
||||
@ -43,13 +44,13 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
|
||||
O.map("sourceReference", S.sourceReference);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(PresentationHint hint) {
|
||||
llvm::json::Value toJSON(Source::PresentationHint hint) {
|
||||
switch (hint) {
|
||||
case ePresentationHintNormal:
|
||||
case Source::eSourcePresentationHintNormal:
|
||||
return "normal";
|
||||
case ePresentationHintEmphasize:
|
||||
case Source::eSourcePresentationHintEmphasize:
|
||||
return "emphasize";
|
||||
case ePresentationHintDeemphasize:
|
||||
case Source::eSourcePresentationHintDeemphasize:
|
||||
return "deemphasize";
|
||||
}
|
||||
llvm_unreachable("unhandled presentation hint.");
|
||||
@ -435,6 +436,90 @@ json::Value toJSON(const Capabilities &C) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &Params, Scope::PresentationHint &PH,
|
||||
json::Path P) {
|
||||
auto rawHint = Params.getAsString();
|
||||
if (!rawHint) {
|
||||
P.report("expected a string");
|
||||
return false;
|
||||
}
|
||||
const std::optional<Scope::PresentationHint> hint =
|
||||
StringSwitch<std::optional<Scope::PresentationHint>>(*rawHint)
|
||||
.Case("arguments", Scope::eScopePresentationHintArguments)
|
||||
.Case("locals", Scope::eScopePresentationHintLocals)
|
||||
.Case("registers", Scope::eScopePresentationHintRegisters)
|
||||
.Case("returnValue", Scope::eScopePresentationHintReturnValue)
|
||||
.Default(std::nullopt);
|
||||
if (!hint) {
|
||||
P.report("unexpected value");
|
||||
return false;
|
||||
}
|
||||
PH = *hint;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &Params, Scope &S, json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("name", S.name) &&
|
||||
O.mapOptional("presentationHint", S.presentationHint) &&
|
||||
O.map("variablesReference", S.variablesReference) &&
|
||||
O.mapOptional("namedVariables", S.namedVariables) &&
|
||||
O.map("indexedVariables", S.indexedVariables) &&
|
||||
O.mapOptional("source", S.source) && O.map("expensive", S.expensive) &&
|
||||
O.mapOptional("line", S.line) && O.mapOptional("column", S.column) &&
|
||||
O.mapOptional("endLine", S.endLine) &&
|
||||
O.mapOptional("endColumn", S.endColumn);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const Scope &SC) {
|
||||
llvm::json::Object result{{"name", SC.name},
|
||||
{"variablesReference", SC.variablesReference},
|
||||
{"expensive", SC.expensive}};
|
||||
|
||||
if (SC.presentationHint.has_value()) {
|
||||
llvm::StringRef presentationHint;
|
||||
switch (*SC.presentationHint) {
|
||||
case Scope::eScopePresentationHintArguments:
|
||||
presentationHint = "arguments";
|
||||
break;
|
||||
case Scope::eScopePresentationHintLocals:
|
||||
presentationHint = "locals";
|
||||
break;
|
||||
case Scope::eScopePresentationHintRegisters:
|
||||
presentationHint = "registers";
|
||||
break;
|
||||
case Scope::eScopePresentationHintReturnValue:
|
||||
presentationHint = "returnValue";
|
||||
break;
|
||||
}
|
||||
|
||||
result.insert({"presentationHint", presentationHint});
|
||||
}
|
||||
|
||||
if (SC.namedVariables.has_value())
|
||||
result.insert({"namedVariables", SC.namedVariables});
|
||||
|
||||
if (SC.indexedVariables.has_value())
|
||||
result.insert({"indexedVariables", SC.indexedVariables});
|
||||
|
||||
if (SC.source.has_value())
|
||||
result.insert({"source", SC.source});
|
||||
|
||||
if (SC.line.has_value())
|
||||
result.insert({"line", SC.line});
|
||||
|
||||
if (SC.column.has_value())
|
||||
result.insert({"column", SC.column});
|
||||
|
||||
if (SC.endLine.has_value())
|
||||
result.insert({"endLine", SC.endLine});
|
||||
|
||||
if (SC.endColumn.has_value())
|
||||
result.insert({"endColumn", SC.endColumn});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, Capabilities &C,
|
||||
llvm::json::Path P) {
|
||||
auto *Object = Params.getAsObject();
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#define LLDB_DAP_INVALID_VARRERF UINT64_MAX
|
||||
|
||||
namespace lldb_dap::protocol {
|
||||
|
||||
/// An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for
|
||||
@ -283,18 +285,16 @@ struct Capabilities {
|
||||
bool fromJSON(const llvm::json::Value &, Capabilities &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const Capabilities &);
|
||||
|
||||
enum PresentationHint : unsigned {
|
||||
ePresentationHintNormal,
|
||||
ePresentationHintEmphasize,
|
||||
ePresentationHintDeemphasize
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, PresentationHint &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(PresentationHint hint);
|
||||
|
||||
/// A `Source` is a descriptor for source code. It is returned from the debug
|
||||
/// adapter as part of a `StackFrame` and it is used by clients when specifying
|
||||
/// breakpoints.
|
||||
struct Source {
|
||||
enum PresentationHint : unsigned {
|
||||
eSourcePresentationHintNormal,
|
||||
eSourcePresentationHintEmphasize,
|
||||
eSourcePresentationHintDeemphasize,
|
||||
};
|
||||
|
||||
/// The short name of the source. Every source returned from the debug adapter
|
||||
/// has a name. When sending a source to the debug adapter this name is
|
||||
/// optional.
|
||||
@ -318,9 +318,82 @@ struct Source {
|
||||
|
||||
// unsupported keys: origin, sources, adapterData, checksums
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, Source::PresentationHint &,
|
||||
llvm::json::Path);
|
||||
llvm::json::Value toJSON(Source::PresentationHint);
|
||||
bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const Source &);
|
||||
|
||||
/// A `Scope` is a named container for variables. Optionally a scope can map to
|
||||
/// a source or a range within a source.
|
||||
struct Scope {
|
||||
enum PresentationHint : unsigned {
|
||||
eScopePresentationHintArguments,
|
||||
eScopePresentationHintLocals,
|
||||
eScopePresentationHintRegisters,
|
||||
eScopePresentationHintReturnValue
|
||||
};
|
||||
/// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This
|
||||
/// string is shown in the UI as is and can be translated.
|
||||
////
|
||||
std::string name;
|
||||
|
||||
/// A hint for how to present this scope in the UI. If this attribute is
|
||||
/// missing, the scope is shown with a generic UI.
|
||||
/// Values:
|
||||
/// 'arguments': Scope contains method arguments.
|
||||
/// 'locals': Scope contains local variables.
|
||||
/// 'registers': Scope contains registers. Only a single `registers` scope
|
||||
/// should be returned from a `scopes` request.
|
||||
/// 'returnValue': Scope contains one or more return values.
|
||||
/// etc.
|
||||
std::optional<PresentationHint> presentationHint;
|
||||
|
||||
/// The variables of this scope can be retrieved by passing the value of
|
||||
/// `variablesReference` to the `variables` request as long as execution
|
||||
/// remains suspended. See 'Lifetime of Object References' in the Overview
|
||||
/// section for details.
|
||||
////
|
||||
uint64_t variablesReference = LLDB_DAP_INVALID_VARRERF;
|
||||
|
||||
/// The number of named variables in this scope.
|
||||
/// The client can use this information to present the variables in a paged UI
|
||||
/// and fetch them in chunks.
|
||||
std::optional<uint64_t> namedVariables;
|
||||
|
||||
/// The number of indexed variables in this scope.
|
||||
/// The client can use this information to present the variables in a paged UI
|
||||
/// and fetch them in chunks.
|
||||
std::optional<uint64_t> indexedVariables;
|
||||
|
||||
/// The source for this scope.
|
||||
std::optional<Source> source;
|
||||
|
||||
/// If true, the number of variables in this scope is large or expensive to
|
||||
/// retrieve.
|
||||
bool expensive = false;
|
||||
|
||||
/// The start line of the range covered by this scope.
|
||||
std::optional<uint64_t> line;
|
||||
|
||||
/// Start position of the range covered by the scope. It is measured in UTF-16
|
||||
/// code units and the client capability `columnsStartAt1` determines whether
|
||||
/// it is 0- or 1-based.
|
||||
std::optional<uint64_t> column;
|
||||
|
||||
/// The end line of the range covered by this scope.
|
||||
std::optional<uint64_t> endLine;
|
||||
|
||||
/// End position of the range covered by the scope. It is measured in UTF-16
|
||||
/// code units and the client capability `columnsStartAt1` determines whether
|
||||
/// it is 0- or 1-based.
|
||||
std::optional<uint64_t> endColumn;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &Params, Scope::PresentationHint &PH,
|
||||
llvm::json::Path);
|
||||
bool fromJSON(const llvm::json::Value &, Scope &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const Scope &);
|
||||
|
||||
/// The granularity of one `step` in the stepping requests `next`, `stepIn`,
|
||||
/// `stepOut` and `stepBack`.
|
||||
enum SteppingGranularity : unsigned {
|
||||
|
@ -50,7 +50,7 @@ TEST(ProtocolTypesTest, Source) {
|
||||
source.name = "testName";
|
||||
source.path = "/path/to/source";
|
||||
source.sourceReference = 12345;
|
||||
source.presentationHint = ePresentationHintEmphasize;
|
||||
source.presentationHint = Source::eSourcePresentationHintEmphasize;
|
||||
|
||||
llvm::Expected<Source> deserialized_source = roundtrip(source);
|
||||
ASSERT_THAT_EXPECTED(deserialized_source, llvm::Succeeded());
|
||||
@ -101,8 +101,8 @@ TEST(ProtocolTypesTest, Breakpoint) {
|
||||
breakpoint.id = 42;
|
||||
breakpoint.verified = true;
|
||||
breakpoint.message = "Breakpoint set successfully";
|
||||
breakpoint.source =
|
||||
Source{"test.cpp", "/path/to/test.cpp", 123, ePresentationHintNormal};
|
||||
breakpoint.source = Source{"test.cpp", "/path/to/test.cpp", 123,
|
||||
Source::eSourcePresentationHintNormal};
|
||||
breakpoint.line = 10;
|
||||
breakpoint.column = 5;
|
||||
breakpoint.endLine = 15;
|
||||
@ -292,12 +292,53 @@ TEST(ProtocolTypesTest, Capabilities) {
|
||||
deserialized_capabilities->lldbExtVersion);
|
||||
}
|
||||
|
||||
TEST(ProtocolTypesTest, Scope) {
|
||||
Scope scope;
|
||||
scope.name = "Locals";
|
||||
scope.presentationHint = Scope::eScopePresentationHintLocals;
|
||||
scope.variablesReference = 1;
|
||||
scope.namedVariables = 2;
|
||||
scope.indexedVariables = std::nullopt;
|
||||
scope.expensive = false;
|
||||
scope.line = 2;
|
||||
scope.column = 3;
|
||||
scope.endLine = 10;
|
||||
scope.endColumn = 20;
|
||||
|
||||
scope.source =
|
||||
Source{.name = "testName",
|
||||
.path = "/path/to/source",
|
||||
.sourceReference = 12345,
|
||||
.presentationHint = Source::eSourcePresentationHintNormal};
|
||||
|
||||
llvm::Expected<Scope> deserialized_scope = roundtrip(scope);
|
||||
ASSERT_THAT_EXPECTED(deserialized_scope, llvm::Succeeded());
|
||||
EXPECT_EQ(scope.name, deserialized_scope->name);
|
||||
EXPECT_EQ(scope.presentationHint, deserialized_scope->presentationHint);
|
||||
EXPECT_EQ(scope.variablesReference, deserialized_scope->variablesReference);
|
||||
EXPECT_EQ(scope.namedVariables, deserialized_scope->namedVariables);
|
||||
EXPECT_EQ(scope.indexedVariables, deserialized_scope->indexedVariables);
|
||||
EXPECT_EQ(scope.expensive, deserialized_scope->expensive);
|
||||
EXPECT_EQ(scope.line, deserialized_scope->line);
|
||||
EXPECT_EQ(scope.column, deserialized_scope->column);
|
||||
EXPECT_EQ(scope.endLine, deserialized_scope->endLine);
|
||||
EXPECT_EQ(scope.endColumn, deserialized_scope->endColumn);
|
||||
|
||||
EXPECT_THAT(deserialized_scope->source.has_value(), true);
|
||||
const Source &source = scope.source.value();
|
||||
const Source &deserialized_source = deserialized_scope->source.value();
|
||||
|
||||
EXPECT_EQ(source.path, deserialized_source.path);
|
||||
EXPECT_EQ(source.sourceReference, deserialized_source.sourceReference);
|
||||
EXPECT_EQ(source.presentationHint, deserialized_source.presentationHint);
|
||||
}
|
||||
|
||||
TEST(ProtocolTypesTest, PresentationHint) {
|
||||
// Test all PresentationHint values.
|
||||
std::vector<std::pair<PresentationHint, llvm::StringRef>> test_cases = {
|
||||
{ePresentationHintNormal, "normal"},
|
||||
{ePresentationHintEmphasize, "emphasize"},
|
||||
{ePresentationHintDeemphasize, "deemphasize"}};
|
||||
std::vector<std::pair<Source::PresentationHint, llvm::StringRef>> test_cases =
|
||||
{{Source::eSourcePresentationHintNormal, "normal"},
|
||||
{Source::eSourcePresentationHintEmphasize, "emphasize"},
|
||||
{Source::eSourcePresentationHintDeemphasize, "deemphasize"}};
|
||||
|
||||
for (const auto &test_case : test_cases) {
|
||||
// Serialize the PresentationHint to JSON.
|
||||
@ -306,7 +347,7 @@ TEST(ProtocolTypesTest, PresentationHint) {
|
||||
EXPECT_EQ(serialized.getAsString(), test_case.second);
|
||||
|
||||
// Deserialize the JSON back to PresentationHint.
|
||||
PresentationHint deserialized;
|
||||
Source::PresentationHint deserialized;
|
||||
llvm::json::Path::Root root;
|
||||
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
|
||||
<< llvm::toString(root.getError());
|
||||
@ -315,7 +356,7 @@ TEST(ProtocolTypesTest, PresentationHint) {
|
||||
|
||||
// Test invalid value.
|
||||
llvm::json::Value invalid_value = "invalid_hint";
|
||||
PresentationHint deserialized_invalid;
|
||||
Source::PresentationHint deserialized_invalid;
|
||||
llvm::json::Path::Root root;
|
||||
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user