-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][lldb-dap] use the new protocol for setVariable requests. #137803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,170 +11,76 @@ | |
#include "JSONUtils.h" | ||
#include "RequestHandler.h" | ||
|
||
using namespace lldb_dap::protocol; | ||
|
||
namespace lldb_dap { | ||
|
||
// "SetVariableRequest": { | ||
// "allOf": [ { "$ref": "#/definitions/Request" }, { | ||
// "type": "object", | ||
// "description": "setVariable request; value of command field is | ||
// 'setVariable'. Set the variable with the given name in the variable | ||
// container to a new value.", "properties": { | ||
// "command": { | ||
// "type": "string", | ||
// "enum": [ "setVariable" ] | ||
// }, | ||
// "arguments": { | ||
// "$ref": "#/definitions/SetVariableArguments" | ||
// } | ||
// }, | ||
// "required": [ "command", "arguments" ] | ||
// }] | ||
// }, | ||
// "SetVariableArguments": { | ||
// "type": "object", | ||
// "description": "Arguments for 'setVariable' request.", | ||
// "properties": { | ||
// "variablesReference": { | ||
// "type": "integer", | ||
// "description": "The reference of the variable container." | ||
// }, | ||
// "name": { | ||
// "type": "string", | ||
// "description": "The name of the variable." | ||
// }, | ||
// "value": { | ||
// "type": "string", | ||
// "description": "The value of the variable." | ||
// }, | ||
// "format": { | ||
// "$ref": "#/definitions/ValueFormat", | ||
// "description": "Specifies details on how to format the response value." | ||
// } | ||
// }, | ||
// "required": [ "variablesReference", "name", "value" ] | ||
// }, | ||
// "SetVariableResponse": { | ||
// "allOf": [ { "$ref": "#/definitions/Response" }, { | ||
// "type": "object", | ||
// "description": "Response to 'setVariable' request.", | ||
// "properties": { | ||
// "body": { | ||
// "type": "object", | ||
// "properties": { | ||
// "value": { | ||
// "type": "string", | ||
// "description": "The new value of the variable." | ||
// }, | ||
// "type": { | ||
// "type": "string", | ||
// "description": "The type of the new value. Typically shown in the | ||
// UI when hovering over the value." | ||
// }, | ||
// "variablesReference": { | ||
// "type": "number", | ||
// "description": "If variablesReference is > 0, the new value is | ||
// structured and its children can be retrieved by passing | ||
// variablesReference to the VariablesRequest." | ||
// }, | ||
// "namedVariables": { | ||
// "type": "number", | ||
// "description": "The number of named child variables. The client | ||
// can use this optional information to present the variables in a | ||
// paged UI and fetch them in chunks." | ||
// }, | ||
// "indexedVariables": { | ||
// "type": "number", | ||
// "description": "The number of indexed child variables. The client | ||
// can use this optional information to present the variables in a | ||
// paged UI and fetch them in chunks." | ||
// }, | ||
// "valueLocationReference": { | ||
// "type": "integer", | ||
// "description": "A reference that allows the client to request the | ||
// location where the new value is declared. For example, if the new | ||
// value is function pointer, the adapter may be able to look up the | ||
// function's location. This should be present only if the adapter | ||
// is likely to be able to resolve the location.\n\nThis reference | ||
// shares the same lifetime as the `variablesReference`. See | ||
// 'Lifetime of Object References' in the Overview section for | ||
// details." | ||
// } | ||
// }, | ||
// "required": [ "value" ] | ||
// } | ||
// }, | ||
// "required": [ "body" ] | ||
// }] | ||
// } | ||
void SetVariableRequestHandler::operator()( | ||
const llvm::json::Object &request) const { | ||
llvm::json::Object response; | ||
FillResponse(request, response); | ||
llvm::json::Array variables; | ||
llvm::json::Object body; | ||
const auto *arguments = request.getObject("arguments"); | ||
// This is a reference to the containing variable/scope | ||
const auto variablesReference = | ||
GetInteger<uint64_t>(arguments, "variablesReference").value_or(0); | ||
llvm::StringRef name = GetString(arguments, "name").value_or(""); | ||
|
||
const auto value = GetString(arguments, "value").value_or(""); | ||
// Set success to false just in case we don't find the variable by name | ||
response.try_emplace("success", false); | ||
|
||
lldb::SBValue variable; | ||
|
||
// 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) { | ||
variable = dap.variables.GetVariable(id_value); | ||
} else { | ||
variable = dap.variables.FindVariable(variablesReference, name); | ||
/// Set the variable with the given name in the variable container to a new | ||
/// value. Clients should only call this request if the corresponding capability | ||
/// `supportsSetVariable` is true. | ||
/// | ||
/// If a debug adapter implements both `setVariable` and `setExpression`, | ||
/// a client will only use `setExpression` if the variable has an evaluateName | ||
/// property. | ||
llvm::Expected<SetVariableResponseBody> | ||
SetVariableRequestHandler::Run(const SetVariableArguments &args) const { | ||
const auto args_name = llvm::StringRef(args.name); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style nit: How about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems more common to have the |
||
|
||
if (args.variablesReference == UINT64_MAX) { | ||
return llvm::make_error<DAPError>( | ||
llvm::formatv("invalid reference {}", args.variablesReference).str(), | ||
llvm::inconvertibleErrorCode(), | ||
/*show_user=*/false); | ||
} | ||
|
||
if (variable.IsValid()) { | ||
lldb::SBError error; | ||
bool success = variable.SetValueFromCString(value.data(), error); | ||
if (success) { | ||
VariableDescription desc(variable, | ||
dap.configuration.enableAutoVariableSummaries); | ||
EmplaceSafeString(body, "value", desc.display_value); | ||
EmplaceSafeString(body, "type", desc.display_type_name); | ||
|
||
// We don't know the index of the variable in our dap.variables | ||
// so always insert a new one to get its variablesReference. | ||
// is_permanent is false because debug console does not support | ||
// setVariable request. | ||
int64_t new_var_ref = | ||
dap.variables.InsertVariable(variable, /*is_permanent=*/false); | ||
if (variable.MightHaveChildren()) | ||
body.try_emplace("variablesReference", new_var_ref); | ||
else | ||
body.try_emplace("variablesReference", 0); | ||
if (lldb::addr_t addr = variable.GetLoadAddress(); | ||
addr != LLDB_INVALID_ADDRESS) | ||
body.try_emplace("memoryReference", EncodeMemoryReference(addr)); | ||
if (ValuePointsToCode(variable)) | ||
body.try_emplace("valueLocationReference", new_var_ref); | ||
} else { | ||
EmplaceSafeString(body, "message", std::string(error.GetCString())); | ||
} | ||
response["success"] = llvm::json::Value(success); | ||
constexpr llvm::StringRef return_value_name = "(Return Value)"; | ||
if (args_name == return_value_name) | ||
return llvm::make_error<DAPError>( | ||
"cannot change the value of the return value"); | ||
da-viper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
lldb::SBValue variable = | ||
dap.variables.FindVariable(args.variablesReference, args_name); | ||
|
||
if (!variable.IsValid()) | ||
return llvm::make_error<DAPError>("could not find variable in scope"); | ||
|
||
lldb::SBError error; | ||
const bool success = variable.SetValueFromCString(args.value.c_str(), error); | ||
if (!success) | ||
return llvm::make_error<DAPError>(error.GetCString()); | ||
|
||
VariableDescription desc(variable, | ||
dap.configuration.enableAutoVariableSummaries); | ||
|
||
SetVariableResponseBody body; | ||
body.value = desc.display_value; | ||
body.type = desc.display_type_name; | ||
|
||
// We don't know the index of the variable in our dap.variables | ||
// so always insert a new one to get its variablesReference. | ||
// is_permanent is false because debug console does not support | ||
// setVariable request. | ||
const int64_t new_var_ref = | ||
dap.variables.InsertVariable(variable, /*is_permanent=*/false); | ||
if (variable.MightHaveChildren()) { | ||
body.variablesReference = new_var_ref; | ||
if (desc.type_obj.IsArrayType()) | ||
body.indexedVariables = variable.GetNumChildren(); | ||
else | ||
body.namedVariables = variable.GetNumChildren(); | ||
|
||
} else { | ||
response["success"] = llvm::json::Value(false); | ||
body.variablesReference = 0; | ||
} | ||
da-viper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
response.try_emplace("body", std::move(body)); | ||
dap.SendJSON(llvm::json::Value(std::move(response))); | ||
if (const lldb::addr_t addr = variable.GetLoadAddress(); | ||
addr != LLDB_INVALID_ADDRESS) | ||
body.memoryReference = EncodeMemoryReference(addr); | ||
|
||
if (ValuePointsToCode(variable)) | ||
body.valueLocationReference = new_var_ref; | ||
|
||
return body; | ||
} | ||
|
||
} // namespace lldb_dap |
Uh oh!
There was an error while loading. Please reload this page.