Skip to content

Commit a20b2bc

Browse files
committed
[lldb-dap] Migrating 'evaluate' to structured types.
Adding structured types for the evaluate request handler. This should be mostly a non-functional change. I did catch some spelling mistakes in our tests ('variable' vs 'variables').
1 parent ea10026 commit a20b2bc

File tree

10 files changed

+317
-207
lines changed

10 files changed

+317
-207
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,9 +978,10 @@ def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None
978978
return []
979979
args_dict = {
980980
"expression": expression,
981-
"context": context,
982981
"frameId": stackFrame["id"],
983982
}
983+
if context:
984+
args_dict["context"] = context
984985
command_dict = {
985986
"command": "evaluate",
986987
"type": "request",

lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Test lldb-dap completions request
2+
Test lldb-dap evaluate request
33
"""
44

55
import re
@@ -245,4 +245,6 @@ def test_hover_evaluate_expressions(self):
245245
@skipIfWindows
246246
def test_variable_evaluate_expressions(self):
247247
# Tests expression evaluations that are triggered in the variable explorer
248-
self.run_test_evaluate_expressions("variable", enableAutoVariableSummaries=True)
248+
self.run_test_evaluate_expressions(
249+
"variables", enableAutoVariableSummaries=True
250+
)

lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp

Lines changed: 78 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -10,148 +10,36 @@
1010
#include "EventHelper.h"
1111
#include "JSONUtils.h"
1212
#include "LLDBUtils.h"
13+
#include "Protocol/ProtocolRequests.h"
14+
#include "Protocol/ProtocolTypes.h"
1315
#include "RequestHandler.h"
16+
#include "lldb/API/SBCommandInterpreter.h"
17+
#include "lldb/API/SBCommandReturnObject.h"
18+
#include "lldb/lldb-enumerations.h"
19+
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/Support/Error.h"
21+
#include <future>
22+
#include <optional>
23+
#include <unistd.h>
24+
25+
using namespace llvm;
26+
using namespace lldb_dap;
27+
using namespace lldb_dap::protocol;
1428

1529
namespace lldb_dap {
1630

17-
// "EvaluateRequest": {
18-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
19-
// "type": "object",
20-
// "description": "Evaluate request; value of command field is 'evaluate'.
21-
// Evaluates the given expression in the context of the
22-
// top most stack frame. The expression has access to any
23-
// variables and arguments that are in scope.",
24-
// "properties": {
25-
// "command": {
26-
// "type": "string",
27-
// "enum": [ "evaluate" ]
28-
// },
29-
// "arguments": {
30-
// "$ref": "#/definitions/EvaluateArguments"
31-
// }
32-
// },
33-
// "required": [ "command", "arguments" ]
34-
// }]
35-
// },
36-
// "EvaluateArguments": {
37-
// "type": "object",
38-
// "description": "Arguments for 'evaluate' request.",
39-
// "properties": {
40-
// "expression": {
41-
// "type": "string",
42-
// "description": "The expression to evaluate."
43-
// },
44-
// "frameId": {
45-
// "type": "integer",
46-
// "description": "Evaluate the expression in the scope of this stack
47-
// frame. If not specified, the expression is evaluated
48-
// in the global scope."
49-
// },
50-
// "context": {
51-
// "type": "string",
52-
// "_enum": [ "watch", "repl", "hover" ],
53-
// "enumDescriptions": [
54-
// "evaluate is run in a watch.",
55-
// "evaluate is run from REPL console.",
56-
// "evaluate is run from a data hover."
57-
// ],
58-
// "description": "The context in which the evaluate request is run."
59-
// },
60-
// "format": {
61-
// "$ref": "#/definitions/ValueFormat",
62-
// "description": "Specifies details on how to format the Evaluate
63-
// result."
64-
// }
65-
// },
66-
// "required": [ "expression" ]
67-
// },
68-
// "EvaluateResponse": {
69-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
70-
// "type": "object",
71-
// "description": "Response to 'evaluate' request.",
72-
// "properties": {
73-
// "body": {
74-
// "type": "object",
75-
// "properties": {
76-
// "result": {
77-
// "type": "string",
78-
// "description": "The result of the evaluate request."
79-
// },
80-
// "type": {
81-
// "type": "string",
82-
// "description": "The optional type of the evaluate result."
83-
// },
84-
// "presentationHint": {
85-
// "$ref": "#/definitions/VariablePresentationHint",
86-
// "description": "Properties of a evaluate result that can be
87-
// used to determine how to render the result in
88-
// the UI."
89-
// },
90-
// "variablesReference": {
91-
// "type": "number",
92-
// "description": "If variablesReference is > 0, the evaluate
93-
// result is structured and its children can be
94-
// retrieved by passing variablesReference to the
95-
// VariablesRequest."
96-
// },
97-
// "namedVariables": {
98-
// "type": "number",
99-
// "description": "The number of named child variables. The
100-
// client can use this optional information to
101-
// present the variables in a paged UI and fetch
102-
// them in chunks."
103-
// },
104-
// "indexedVariables": {
105-
// "type": "number",
106-
// "description": "The number of indexed child variables. The
107-
// client can use this optional information to
108-
// present the variables in a paged UI and fetch
109-
// them in chunks."
110-
// },
111-
// "valueLocationReference": {
112-
// "type": "integer",
113-
// "description": "A reference that allows the client to request
114-
// the location where the returned value is
115-
// declared. For example, if a function pointer is
116-
// returned, the adapter may be able to look up the
117-
// function's location. This should be present only
118-
// if the adapter is likely to be able to resolve
119-
// the location.\n\nThis reference shares the same
120-
// lifetime as the `variablesReference`. See
121-
// 'Lifetime of Object References' in the
122-
// Overview section for details."
123-
// }
124-
// "memoryReference": {
125-
// "type": "string",
126-
// "description": "A memory reference to a location appropriate
127-
// for this result. For pointer type eval
128-
// results, this is generally a reference to the
129-
// memory address contained in the pointer. This
130-
// attribute may be returned by a debug adapter
131-
// if corresponding capability
132-
// `supportsMemoryReferences` is true."
133-
// },
134-
// },
135-
// "required": [ "result", "variablesReference" ]
136-
// }
137-
// },
138-
// "required": [ "body" ]
139-
// }]
140-
// }
141-
void EvaluateRequestHandler::operator()(
142-
const llvm::json::Object &request) const {
143-
llvm::json::Object response;
144-
FillResponse(request, response);
145-
llvm::json::Object body;
146-
const auto *arguments = request.getObject("arguments");
147-
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
148-
std::string expression =
149-
GetString(arguments, "expression").value_or("").str();
150-
const llvm::StringRef context = GetString(arguments, "context").value_or("");
31+
/// Evaluates the given expression in the context of a stack frame.
32+
///
33+
/// The expression has access to any variables and arguments that are in scope.
34+
Expected<EvaluateResponseBody>
35+
EvaluateRequestHandler::Run(const EvaluateArguments &arguments) const {
36+
EvaluateResponseBody body;
37+
lldb::SBFrame frame = dap.GetLLDBFrame(arguments.frameId);
38+
std::string expression = arguments.expression;
15139
bool repeat_last_command =
15240
expression.empty() && dap.last_nonempty_var_expression.empty();
15341

154-
if (context == "repl" &&
42+
if (arguments.context == protocol::eEvaluateContextRepl &&
15543
(repeat_last_command ||
15644
(!expression.empty() &&
15745
dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
@@ -169,66 +57,61 @@ void EvaluateRequestHandler::operator()(
16957
dap.debugger, llvm::StringRef(), {expression}, required_command_failed,
17058
/*parse_command_directives=*/false, /*echo_commands=*/false);
17159

172-
EmplaceSafeString(body, "result", result);
173-
body.try_emplace("variablesReference", (int64_t)0);
174-
} else {
175-
if (context == "repl") {
176-
// If the expression is empty and the last expression was for a
177-
// variable, set the expression to the previous expression (repeat the
178-
// evaluation); otherwise save the current non-empty expression for the
179-
// next (possibly empty) variable expression.
180-
if (expression.empty())
181-
expression = dap.last_nonempty_var_expression;
182-
else
183-
dap.last_nonempty_var_expression = expression;
184-
}
185-
// Always try to get the answer from the local variables if possible. If
186-
// this fails, then if the context is not "hover", actually evaluate an
187-
// expression using the expression parser.
188-
//
189-
// "frame variable" is more reliable than the expression parser in
190-
// many cases and it is faster.
191-
lldb::SBValue value = frame.GetValueForVariablePath(
192-
expression.data(), lldb::eDynamicDontRunTarget);
193-
194-
// Freeze dry the value in case users expand it later in the debug console
195-
if (value.GetError().Success() && context == "repl")
196-
value = value.Persist();
197-
198-
if (value.GetError().Fail() && context != "hover")
199-
value = frame.EvaluateExpression(expression.data());
200-
201-
if (value.GetError().Fail()) {
202-
response["success"] = llvm::json::Value(false);
203-
// This error object must live until we're done with the pointer returned
204-
// by GetCString().
205-
lldb::SBError error = value.GetError();
206-
const char *error_cstr = error.GetCString();
207-
if (error_cstr && error_cstr[0])
208-
EmplaceSafeString(response, "message", error_cstr);
209-
else
210-
EmplaceSafeString(response, "message", "evaluate failed");
211-
} else {
212-
VariableDescription desc(value,
213-
dap.configuration.enableAutoVariableSummaries);
214-
EmplaceSafeString(body, "result", desc.GetResult(context));
215-
EmplaceSafeString(body, "type", desc.display_type_name);
216-
int64_t var_ref = 0;
217-
if (value.MightHaveChildren() || ValuePointsToCode(value))
218-
var_ref = dap.variables.InsertVariable(
219-
value, /*is_permanent=*/context == "repl");
220-
if (value.MightHaveChildren())
221-
body.try_emplace("variablesReference", var_ref);
222-
else
223-
body.try_emplace("variablesReference", (int64_t)0);
224-
if (lldb::addr_t addr = value.GetLoadAddress();
225-
addr != LLDB_INVALID_ADDRESS)
226-
body.try_emplace("memoryReference", EncodeMemoryReference(addr));
227-
if (ValuePointsToCode(value))
228-
body.try_emplace("valueLocationReference", var_ref);
229-
}
60+
body.result = result;
61+
return body;
62+
}
63+
64+
if (arguments.context == eEvaluateContextRepl) {
65+
// If the expression is empty and the last expression was for a
66+
// variable, set the expression to the previous expression (repeat the
67+
// evaluation); otherwise save the current non-empty expression for the
68+
// next (possibly empty) variable expression.
69+
if (expression.empty())
70+
expression = dap.last_nonempty_var_expression;
71+
else
72+
dap.last_nonempty_var_expression = expression;
73+
}
74+
75+
// Always try to get the answer from the local variables if possible. If
76+
// this fails, then if the context is not "hover", actually evaluate an
77+
// expression using the expression parser.
78+
//
79+
// "frame variable" is more reliable than the expression parser in
80+
// many cases and it is faster.
81+
lldb::SBValue value = frame.GetValueForVariablePath(
82+
expression.data(), lldb::eDynamicDontRunTarget);
83+
84+
// Freeze dry the value in case users expand it later in the debug console
85+
if (value.GetError().Success() && arguments.context == eEvaluateContextRepl)
86+
value = value.Persist();
87+
88+
if (value.GetError().Fail() && arguments.context != eEvaluateContextHover)
89+
value = frame.EvaluateExpression(expression.data());
90+
91+
if (value.GetError().Fail()) {
92+
// This error object must live until we're done with the pointer returned
93+
// by GetCString().
94+
lldb::SBError error = value.GetError();
95+
const char *error_cstr = error.GetCString();
96+
return make_error<DAPError>(
97+
error_cstr && error_cstr[0] ? error_cstr : "evaluate failed");
23098
}
231-
response.try_emplace("body", std::move(body));
232-
dap.SendJSON(llvm::json::Value(std::move(response)));
99+
100+
VariableDescription desc(value,
101+
dap.configuration.enableAutoVariableSummaries);
102+
body.result = desc.GetResult(arguments.context);
103+
body.type = desc.display_type_name;
104+
if (value.MightHaveChildren() || ValuePointsToCode(value))
105+
body.variablesReference = dap.variables.InsertVariable(
106+
value, /*is_permanent=*/arguments.context == eEvaluateContextRepl);
107+
if (lldb::addr_t addr = value.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
108+
body.memoryReference = EncodeMemoryReference(addr);
109+
110+
if (ValuePointsToCode(value) &&
111+
body.variablesReference != LLDB_DAP_INVALID_VARRERF)
112+
body.valueLocationReference = body.variablesReference;
113+
114+
return body;
233115
}
116+
234117
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,14 @@ class DisconnectRequestHandler
292292
Run(const std::optional<protocol::DisconnectArguments> &args) const override;
293293
};
294294

295-
class EvaluateRequestHandler : public LegacyRequestHandler {
295+
class EvaluateRequestHandler
296+
: public RequestHandler<protocol::EvaluateArguments,
297+
llvm::Expected<protocol::EvaluateResponseBody>> {
296298
public:
297-
using LegacyRequestHandler::LegacyRequestHandler;
299+
using RequestHandler::RequestHandler;
298300
static llvm::StringLiteral GetCommand() { return "evaluate"; }
299-
void operator()(const llvm::json::Object &request) const override;
301+
llvm::Expected<protocol::EvaluateResponseBody>
302+
Run(const protocol::EvaluateArguments &) const override;
300303
FeatureSet GetSupportedFeatures() const override {
301304
return {protocol::eAdapterFeatureEvaluateForHovers};
302305
}

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ExceptionBreakpoint.h"
1212
#include "LLDBUtils.h"
1313
#include "Protocol/ProtocolBase.h"
14+
#include "Protocol/ProtocolRequests.h"
1415
#include "ProtocolUtils.h"
1516
#include "lldb/API/SBAddress.h"
1617
#include "lldb/API/SBCompileUnit.h"
@@ -817,10 +818,10 @@ VariableDescription::VariableDescription(lldb::SBValue v,
817818
evaluate_name = llvm::StringRef(evaluateStream.GetData()).str();
818819
}
819820

820-
std::string VariableDescription::GetResult(llvm::StringRef context) {
821+
std::string VariableDescription::GetResult(protocol::EvaluateContext context) {
821822
// In repl context, the results can be displayed as multiple lines so more
822823
// detailed descriptions can be returned.
823-
if (context != "repl")
824+
if (context != protocol::eEvaluateContextRepl)
824825
return display_value;
825826

826827
if (!v.IsValid())

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
1111

1212
#include "DAPForward.h"
13-
#include "Protocol/ProtocolTypes.h"
13+
#include "Protocol/ProtocolRequests.h"
1414
#include "lldb/API/SBCompileUnit.h"
1515
#include "lldb/API/SBFormat.h"
1616
#include "lldb/API/SBType.h"
@@ -28,7 +28,7 @@
2828

2929
namespace lldb_dap {
3030

31-
/// Emplace a StringRef in a json::Object after enusring that the
31+
/// Emplace a StringRef in a json::Object after ensuring that the
3232
/// string is valid UTF8. If not, first call llvm::json::fixUTF8
3333
/// before emplacing.
3434
///
@@ -351,7 +351,7 @@ struct VariableDescription {
351351
std::optional<std::string> custom_name = {});
352352

353353
/// Returns a description of the value appropriate for the specified context.
354-
std::string GetResult(llvm::StringRef context);
354+
std::string GetResult(protocol::EvaluateContext context);
355355
};
356356

357357
/// Does the given variable have an associated value location?

0 commit comments

Comments
 (0)