Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e8adb3a

Browse files
authored
[lldb][lldb-dap] use the new protocol for setVariable requests. (#137803)
```cpp // The "id" is the unique integer ID that is unique within the enclosing // variablesReference. It is optionally added to any "interface Variable" // objects to uniquely identify a variable within an enclosing // variablesReference. It helps to disambiguate between two variables that // have the same name within the same scope since the "setVariables" request // only specifies the variable reference of the enclosing scope/variable, and // the name of the variable. We could have two shadowed variables with the // same name in "Locals" or "Globals". In our case the "id" absolute index // of the variable within the dap.variables list. const auto id_value = GetInteger<uint64_t>(arguments, "id").value_or(UINT64_MAX); if (id_value != UINT64_MAX) { ``` I dropped this part because. variables that have the same name has a ` @path` suffix on both of them. and the setVariableArguments does not have a field called `id`.
1 parent 027b203 commit e8adb3a

File tree

7 files changed

+187
-161
lines changed

7 files changed

+187
-161
lines changed

lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py

+4
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,10 @@ def test_return_variables(self):
722722
self.verify_variables(verify_locals, local_variables, varref_dict)
723723
break
724724

725+
self.assertFalse(
726+
self.dap_server.request_setVariable(1, "(Return Value)", 20)["success"]
727+
)
728+
725729
@skipIfWindows
726730
def test_indexedVariables(self):
727731
self.do_test_indexedVariables(enableSyntheticChildDebugging=False)

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

+7-4
Original file line numberDiff line numberDiff line change
@@ -430,17 +430,20 @@ class ScopesRequestHandler : public LegacyRequestHandler {
430430
void operator()(const llvm::json::Object &request) const override;
431431
};
432432

433-
class SetVariableRequestHandler : public LegacyRequestHandler {
433+
class SetVariableRequestHandler final
434+
: public RequestHandler<protocol::SetVariableArguments,
435+
llvm::Expected<protocol::SetVariableResponseBody>> {
434436
public:
435-
using LegacyRequestHandler::LegacyRequestHandler;
437+
using RequestHandler::RequestHandler;
436438
static llvm::StringLiteral GetCommand() { return "setVariable"; }
437439
FeatureSet GetSupportedFeatures() const override {
438440
return {protocol::eAdapterFeatureSetVariable};
439441
}
440-
void operator()(const llvm::json::Object &request) const override;
442+
llvm::Expected<protocol::SetVariableResponseBody>
443+
Run(const protocol::SetVariableArguments &args) const override;
441444
};
442445

443-
class SourceRequestHandler
446+
class SourceRequestHandler final
444447
: public RequestHandler<protocol::SourceArguments,
445448
llvm::Expected<protocol::SourceResponseBody>> {
446449
public:

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

+63-157
Original file line numberDiff line numberDiff line change
@@ -11,170 +11,76 @@
1111
#include "JSONUtils.h"
1212
#include "RequestHandler.h"
1313

14+
using namespace lldb_dap::protocol;
15+
1416
namespace lldb_dap {
1517

16-
// "SetVariableRequest": {
17-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
18-
// "type": "object",
19-
// "description": "setVariable request; value of command field is
20-
// 'setVariable'. Set the variable with the given name in the variable
21-
// container to a new value.", "properties": {
22-
// "command": {
23-
// "type": "string",
24-
// "enum": [ "setVariable" ]
25-
// },
26-
// "arguments": {
27-
// "$ref": "#/definitions/SetVariableArguments"
28-
// }
29-
// },
30-
// "required": [ "command", "arguments" ]
31-
// }]
32-
// },
33-
// "SetVariableArguments": {
34-
// "type": "object",
35-
// "description": "Arguments for 'setVariable' request.",
36-
// "properties": {
37-
// "variablesReference": {
38-
// "type": "integer",
39-
// "description": "The reference of the variable container."
40-
// },
41-
// "name": {
42-
// "type": "string",
43-
// "description": "The name of the variable."
44-
// },
45-
// "value": {
46-
// "type": "string",
47-
// "description": "The value of the variable."
48-
// },
49-
// "format": {
50-
// "$ref": "#/definitions/ValueFormat",
51-
// "description": "Specifies details on how to format the response value."
52-
// }
53-
// },
54-
// "required": [ "variablesReference", "name", "value" ]
55-
// },
56-
// "SetVariableResponse": {
57-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
58-
// "type": "object",
59-
// "description": "Response to 'setVariable' request.",
60-
// "properties": {
61-
// "body": {
62-
// "type": "object",
63-
// "properties": {
64-
// "value": {
65-
// "type": "string",
66-
// "description": "The new value of the variable."
67-
// },
68-
// "type": {
69-
// "type": "string",
70-
// "description": "The type of the new value. Typically shown in the
71-
// UI when hovering over the value."
72-
// },
73-
// "variablesReference": {
74-
// "type": "number",
75-
// "description": "If variablesReference is > 0, the new value is
76-
// structured and its children can be retrieved by passing
77-
// variablesReference to the VariablesRequest."
78-
// },
79-
// "namedVariables": {
80-
// "type": "number",
81-
// "description": "The number of named child variables. The client
82-
// can use this optional information to present the variables in a
83-
// paged UI and fetch them in chunks."
84-
// },
85-
// "indexedVariables": {
86-
// "type": "number",
87-
// "description": "The number of indexed child variables. The client
88-
// can use this optional information to present the variables in a
89-
// paged UI and fetch them in chunks."
90-
// },
91-
// "valueLocationReference": {
92-
// "type": "integer",
93-
// "description": "A reference that allows the client to request the
94-
// location where the new value is declared. For example, if the new
95-
// value is function pointer, the adapter may be able to look up the
96-
// function's location. This should be present only if the adapter
97-
// is likely to be able to resolve the location.\n\nThis reference
98-
// shares the same lifetime as the `variablesReference`. See
99-
// 'Lifetime of Object References' in the Overview section for
100-
// details."
101-
// }
102-
// },
103-
// "required": [ "value" ]
104-
// }
105-
// },
106-
// "required": [ "body" ]
107-
// }]
108-
// }
109-
void SetVariableRequestHandler::operator()(
110-
const llvm::json::Object &request) const {
111-
llvm::json::Object response;
112-
FillResponse(request, response);
113-
llvm::json::Array variables;
114-
llvm::json::Object body;
115-
const auto *arguments = request.getObject("arguments");
116-
// This is a reference to the containing variable/scope
117-
const auto variablesReference =
118-
GetInteger<uint64_t>(arguments, "variablesReference").value_or(0);
119-
llvm::StringRef name = GetString(arguments, "name").value_or("");
120-
121-
const auto value = GetString(arguments, "value").value_or("");
122-
// Set success to false just in case we don't find the variable by name
123-
response.try_emplace("success", false);
124-
125-
lldb::SBValue variable;
126-
127-
// The "id" is the unique integer ID that is unique within the enclosing
128-
// variablesReference. It is optionally added to any "interface Variable"
129-
// objects to uniquely identify a variable within an enclosing
130-
// variablesReference. It helps to disambiguate between two variables that
131-
// have the same name within the same scope since the "setVariables" request
132-
// only specifies the variable reference of the enclosing scope/variable, and
133-
// the name of the variable. We could have two shadowed variables with the
134-
// same name in "Locals" or "Globals". In our case the "id" absolute index
135-
// of the variable within the dap.variables list.
136-
const auto id_value =
137-
GetInteger<uint64_t>(arguments, "id").value_or(UINT64_MAX);
138-
if (id_value != UINT64_MAX) {
139-
variable = dap.variables.GetVariable(id_value);
140-
} else {
141-
variable = dap.variables.FindVariable(variablesReference, name);
18+
/// Set the variable with the given name in the variable container to a new
19+
/// value. Clients should only call this request if the corresponding capability
20+
/// `supportsSetVariable` is true.
21+
///
22+
/// If a debug adapter implements both `setVariable` and `setExpression`,
23+
/// a client will only use `setExpression` if the variable has an evaluateName
24+
/// property.
25+
llvm::Expected<SetVariableResponseBody>
26+
SetVariableRequestHandler::Run(const SetVariableArguments &args) const {
27+
const auto args_name = llvm::StringRef(args.name);
28+
29+
if (args.variablesReference == UINT64_MAX) {
30+
return llvm::make_error<DAPError>(
31+
llvm::formatv("invalid reference {}", args.variablesReference).str(),
32+
llvm::inconvertibleErrorCode(),
33+
/*show_user=*/false);
14234
}
14335

144-
if (variable.IsValid()) {
145-
lldb::SBError error;
146-
bool success = variable.SetValueFromCString(value.data(), error);
147-
if (success) {
148-
VariableDescription desc(variable,
149-
dap.configuration.enableAutoVariableSummaries);
150-
EmplaceSafeString(body, "value", desc.display_value);
151-
EmplaceSafeString(body, "type", desc.display_type_name);
152-
153-
// We don't know the index of the variable in our dap.variables
154-
// so always insert a new one to get its variablesReference.
155-
// is_permanent is false because debug console does not support
156-
// setVariable request.
157-
int64_t new_var_ref =
158-
dap.variables.InsertVariable(variable, /*is_permanent=*/false);
159-
if (variable.MightHaveChildren())
160-
body.try_emplace("variablesReference", new_var_ref);
161-
else
162-
body.try_emplace("variablesReference", 0);
163-
if (lldb::addr_t addr = variable.GetLoadAddress();
164-
addr != LLDB_INVALID_ADDRESS)
165-
body.try_emplace("memoryReference", EncodeMemoryReference(addr));
166-
if (ValuePointsToCode(variable))
167-
body.try_emplace("valueLocationReference", new_var_ref);
168-
} else {
169-
EmplaceSafeString(body, "message", std::string(error.GetCString()));
170-
}
171-
response["success"] = llvm::json::Value(success);
36+
constexpr llvm::StringRef return_value_name = "(Return Value)";
37+
if (args_name == return_value_name)
38+
return llvm::make_error<DAPError>(
39+
"cannot change the value of the return value");
40+
41+
lldb::SBValue variable =
42+
dap.variables.FindVariable(args.variablesReference, args_name);
43+
44+
if (!variable.IsValid())
45+
return llvm::make_error<DAPError>("could not find variable in scope");
46+
47+
lldb::SBError error;
48+
const bool success = variable.SetValueFromCString(args.value.c_str(), error);
49+
if (!success)
50+
return llvm::make_error<DAPError>(error.GetCString());
51+
52+
VariableDescription desc(variable,
53+
dap.configuration.enableAutoVariableSummaries);
54+
55+
SetVariableResponseBody body;
56+
body.value = desc.display_value;
57+
body.type = desc.display_type_name;
58+
59+
// We don't know the index of the variable in our dap.variables
60+
// so always insert a new one to get its variablesReference.
61+
// is_permanent is false because debug console does not support
62+
// setVariable request.
63+
const int64_t new_var_ref =
64+
dap.variables.InsertVariable(variable, /*is_permanent=*/false);
65+
if (variable.MightHaveChildren()) {
66+
body.variablesReference = new_var_ref;
67+
if (desc.type_obj.IsArrayType())
68+
body.indexedVariables = variable.GetNumChildren();
69+
else
70+
body.namedVariables = variable.GetNumChildren();
71+
17272
} else {
173-
response["success"] = llvm::json::Value(false);
73+
body.variablesReference = 0;
17474
}
17575

176-
response.try_emplace("body", std::move(body));
177-
dap.SendJSON(llvm::json::Value(std::move(response)));
76+
if (const lldb::addr_t addr = variable.GetLoadAddress();
77+
addr != LLDB_INVALID_ADDRESS)
78+
body.memoryReference = EncodeMemoryReference(addr);
79+
80+
if (ValuePointsToCode(variable))
81+
body.valueLocationReference = new_var_ref;
82+
83+
return body;
17884
}
17985

18086
} // namespace lldb_dap

lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,37 @@ bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
261261
parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P);
262262
}
263263

264+
bool fromJSON(const llvm::json::Value &Params, SetVariableArguments &SVA,
265+
llvm::json::Path P) {
266+
json::ObjectMapper O(Params, P);
267+
return O && O.map("variablesReference", SVA.variablesReference) &&
268+
O.map("name", SVA.name) && O.map("value", SVA.value) &&
269+
O.mapOptional("format", SVA.format);
270+
}
271+
272+
llvm::json::Value toJSON(const SetVariableResponseBody &SVR) {
273+
json::Object Body{{"value", SVR.value}};
274+
if (SVR.type.has_value())
275+
Body.insert({"type", SVR.type});
276+
277+
if (SVR.variablesReference.has_value())
278+
Body.insert({"variablesReference", SVR.variablesReference});
279+
280+
if (SVR.namedVariables.has_value())
281+
Body.insert({"namedVariables", SVR.namedVariables});
282+
283+
if (SVR.indexedVariables.has_value())
284+
Body.insert({"indexedVariables", SVR.indexedVariables});
285+
286+
if (SVR.memoryReference.has_value())
287+
Body.insert({"memoryReference", SVR.memoryReference});
288+
289+
if (SVR.valueLocationReference.has_value())
290+
Body.insert({"valueLocationReference", SVR.valueLocationReference});
291+
292+
return llvm::json::Value(std::move(Body));
293+
}
294+
264295
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
265296
json::ObjectMapper O(Params, P);
266297
return O && O.map("source", SA.source) &&

lldb/tools/lldb-dap/Protocol/ProtocolRequests.h

+69
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,75 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
294294
/// field is required.
295295
using LaunchResponseBody = VoidResponse;
296296

297+
/// Arguments for `setVariable` request.
298+
struct SetVariableArguments {
299+
/// The reference of the variable container. The `variablesReference` must
300+
/// have been obtained in the current suspended state. See 'Lifetime of Object
301+
/// References' in the Overview section for details.
302+
uint64_t variablesReference = UINT64_MAX;
303+
304+
/// The name of the variable in the container.
305+
std::string name;
306+
307+
/// The value of the variable.
308+
std::string value;
309+
310+
/// Specifies details on how to format the response value.
311+
ValueFormat format;
312+
};
313+
bool fromJSON(const llvm::json::Value &, SetVariableArguments &,
314+
llvm::json::Path);
315+
316+
/// Response to `setVariable` request.
317+
struct SetVariableResponseBody {
318+
319+
/// The new value of the variable.
320+
std::string value;
321+
322+
/// The type of the new value. Typically shown in the UI when hovering over
323+
/// the value.
324+
std::optional<std::string> type;
325+
326+
/// If `variablesReference` is > 0, the new value is structured and its
327+
/// children can be retrieved by passing `variablesReference` to the
328+
/// `variables` request as long as execution remains suspended. See 'Lifetime
329+
/// of Object References' in the Overview section for details.
330+
///
331+
/// If this property is included in the response, any `variablesReference`
332+
/// previously associated with the updated variable, and those of its
333+
/// children, are no longer valid.
334+
std::optional<uint64_t> variablesReference;
335+
336+
/// The number of named child variables.
337+
/// The client can use this information to present the variables in a paged
338+
/// UI and fetch them in chunks.
339+
/// The value should be less than or equal to 2147483647 (2^31-1).
340+
std::optional<uint32_t> namedVariables;
341+
342+
/// The number of indexed child variables.
343+
/// The client can use this information to present the variables in a paged
344+
/// UI and fetch them in chunks.
345+
/// The value should be less than or equal to 2147483647 (2^31-1).
346+
std::optional<uint32_t> indexedVariables;
347+
348+
/// A memory reference to a location appropriate for this result.
349+
/// For pointer type eval results, this is generally a reference to the
350+
/// memory address contained in the pointer.
351+
/// This attribute may be returned by a debug adapter if corresponding
352+
/// capability `supportsMemoryReferences` is true.
353+
std::optional<std::string> memoryReference;
354+
355+
/// A reference that allows the client to request the location where the new
356+
/// value is declared. For example, if the new value is function pointer, the
357+
/// adapter may be able to look up the function's location. This should be
358+
/// present only if the adapter is likely to be able to resolve the location.
359+
///
360+
/// This reference shares the same lifetime as the `variablesReference`. See
361+
/// 'Lifetime of Object References' in the Overview section for details.
362+
std::optional<uint64_t> valueLocationReference;
363+
};
364+
llvm::json::Value toJSON(const SetVariableResponseBody &);
365+
297366
/// Arguments for `source` request.
298367
struct SourceArguments {
299368
/// Specifies the source content to load. Either `source.path` or

lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,10 @@ bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG,
254254
return true;
255255
}
256256

257+
bool fromJSON(const llvm::json::Value &Params, ValueFormat &VF,
258+
llvm::json::Path P) {
259+
json::ObjectMapper O(Params, P);
260+
return O && O.mapOptional("hex", VF.hex);
261+
}
262+
257263
} // namespace lldb_dap::protocol

0 commit comments

Comments
 (0)