While looking at the '[raw]' value of a std::vector I noticed we didn't handle the anonymous inner struct very well. The 'evaluateName' was incorrect (e.g. the evaluateName would return `<var>.` for the anonymous struct). This improves support for variables with anonymous fields and anonymous types. * Changed the name of anonymous fields from `<null>` to `(anonymous)`, which matches other tooling like clangd's representation and how types are presented if the field is not defined. * Adjusts variables to not return an 'evaluateName' for anonymous fields. * Adjusted '[raw]' values to be marked as 'internal' which deemphasizes them in the UI. While working in this area, I also consolidated some helpers that are only used within Variables.cpp. Before my changes: <img width="513" height="460" alt="before" src="https://github.com/user-attachments/assets/3da0aada-8ba3-415d-bbec-56b41a9b9415" /> After my changes: <img width="414" height="467" alt="after" src="https://github.com/user-attachments/assets/66a47108-ee44-4e01-8eab-e89edb348fde" />
114 lines
4.1 KiB
C++
114 lines
4.1 KiB
C++
//===-- SetVariableRequestHandler.cpp -------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DAP.h"
|
|
#include "EventHelper.h"
|
|
#include "JSONUtils.h"
|
|
#include "Protocol/DAPTypes.h"
|
|
#include "Protocol/ProtocolEvents.h"
|
|
#include "Protocol/ProtocolTypes.h"
|
|
#include "RequestHandler.h"
|
|
|
|
using namespace lldb_dap::protocol;
|
|
|
|
namespace lldb_dap {
|
|
|
|
static lldb::SBValue EvaluateExpression(lldb::SBTarget &target,
|
|
lldb::SBFrame &frame,
|
|
const std::string &expression) {
|
|
const char *expression_cstr = expression.c_str();
|
|
|
|
if (frame)
|
|
return frame.EvaluateExpression(expression_cstr);
|
|
|
|
// Evaluate expression in global scope.
|
|
return target.EvaluateExpression(expression_cstr);
|
|
}
|
|
|
|
/// 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);
|
|
|
|
if (args.variablesReference.Kind() == eReferenceKindInvalid) {
|
|
return llvm::make_error<DAPError>(
|
|
llvm::formatv("invalid reference {}",
|
|
args.variablesReference.AsUInt32())
|
|
.str(),
|
|
llvm::inconvertibleErrorCode(),
|
|
/*show_user=*/false);
|
|
}
|
|
|
|
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");
|
|
|
|
lldb::SBValue variable =
|
|
dap.reference_storage.FindVariable(args.variablesReference, args_name);
|
|
|
|
if (!variable.IsValid())
|
|
return llvm::make_error<DAPError>("could not find variable in scope");
|
|
|
|
lldb::SBFrame frame = variable.GetFrame();
|
|
std::string expression = llvm::StringRef(args.value).trim().str();
|
|
lldb::SBValue result = EvaluateExpression(dap.target, frame, expression);
|
|
const char *value = result.IsValid() ? result.GetValue() : expression.c_str();
|
|
|
|
lldb::SBError error;
|
|
const bool success = variable.SetValueFromCString(value, error);
|
|
if (!success)
|
|
return llvm::make_error<DAPError>(error.GetCString());
|
|
|
|
const bool hex = args.format ? args.format->hex : false;
|
|
VariableDescription desc(variable,
|
|
dap.configuration.enableAutoVariableSummaries, hex);
|
|
|
|
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 var_ref_t new_var_ref = dap.reference_storage.Insert(
|
|
variable, /*is_permanent=*/false, /*is_internal=*/false);
|
|
if (variable.MightHaveChildren()) {
|
|
body.variablesReference = new_var_ref;
|
|
if (desc.type_obj.IsArrayType())
|
|
body.indexedVariables = variable.GetNumChildren();
|
|
else
|
|
body.namedVariables = variable.GetNumChildren();
|
|
}
|
|
|
|
if (const lldb::addr_t addr = variable.GetLoadAddress();
|
|
addr != LLDB_INVALID_ADDRESS)
|
|
body.memoryReference = addr;
|
|
|
|
if (ValuePointsToCode(variable))
|
|
body.valueLocationReference = PackLocation(new_var_ref.AsUInt32(), true);
|
|
|
|
// Also send invalidated event to signal client that some variables
|
|
// (e.g. references) can be changed.
|
|
SendInvalidatedEvent(dap, {InvalidatedEventBody::eAreaVariables});
|
|
|
|
// Also send memory event to signal client that variable memory was changed.
|
|
SendMemoryEvent(dap, variable);
|
|
|
|
return body;
|
|
}
|
|
|
|
} // namespace lldb_dap
|