//===-- 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 SetVariableRequestHandler::Run(const SetVariableArguments &args) const { const auto args_name = llvm::StringRef(args.name); if (args.variablesReference.Kind() == eReferenceKindInvalid) { return llvm::make_error( 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( "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("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(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