blob: 47a45ab8078541a504a2b66bfffa4d48cb2aa0dc [file] [edit]
//===-- 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