xref: /openbsd-src/gnu/llvm/lldb/source/Core/ValueObjectVariable.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- ValueObjectVariable.cpp -------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Core/ValueObjectVariable.h"
10061da546Spatrick 
11061da546Spatrick #include "lldb/Core/Address.h"
12061da546Spatrick #include "lldb/Core/AddressRange.h"
13be691f3bSpatrick #include "lldb/Core/Declaration.h"
14061da546Spatrick #include "lldb/Core/Module.h"
15061da546Spatrick #include "lldb/Core/Value.h"
16*f6aab3d8Srobert #include "lldb/Expression/DWARFExpressionList.h"
17061da546Spatrick #include "lldb/Symbol/Function.h"
18061da546Spatrick #include "lldb/Symbol/ObjectFile.h"
19061da546Spatrick #include "lldb/Symbol/SymbolContext.h"
20061da546Spatrick #include "lldb/Symbol/SymbolContextScope.h"
21061da546Spatrick #include "lldb/Symbol/Type.h"
22061da546Spatrick #include "lldb/Symbol/Variable.h"
23061da546Spatrick #include "lldb/Target/ExecutionContext.h"
24061da546Spatrick #include "lldb/Target/Process.h"
25061da546Spatrick #include "lldb/Target/RegisterContext.h"
26061da546Spatrick #include "lldb/Target/Target.h"
27061da546Spatrick #include "lldb/Utility/DataExtractor.h"
28061da546Spatrick #include "lldb/Utility/RegisterValue.h"
29061da546Spatrick #include "lldb/Utility/Scalar.h"
30061da546Spatrick #include "lldb/Utility/Status.h"
31061da546Spatrick #include "lldb/lldb-private-enumerations.h"
32061da546Spatrick #include "lldb/lldb-types.h"
33061da546Spatrick 
34061da546Spatrick #include "llvm/ADT/StringRef.h"
35061da546Spatrick 
36be691f3bSpatrick #include <cassert>
37061da546Spatrick #include <memory>
38*f6aab3d8Srobert #include <optional>
39061da546Spatrick 
40061da546Spatrick namespace lldb_private {
41061da546Spatrick class ExecutionContextScope;
42061da546Spatrick }
43061da546Spatrick namespace lldb_private {
44061da546Spatrick class StackFrame;
45061da546Spatrick }
46061da546Spatrick namespace lldb_private {
47061da546Spatrick struct RegisterInfo;
48061da546Spatrick }
49061da546Spatrick using namespace lldb_private;
50061da546Spatrick 
51061da546Spatrick lldb::ValueObjectSP
Create(ExecutionContextScope * exe_scope,const lldb::VariableSP & var_sp)52061da546Spatrick ValueObjectVariable::Create(ExecutionContextScope *exe_scope,
53061da546Spatrick                             const lldb::VariableSP &var_sp) {
54dda28197Spatrick   auto manager_sp = ValueObjectManager::Create();
55dda28197Spatrick   return (new ValueObjectVariable(exe_scope, *manager_sp, var_sp))->GetSP();
56061da546Spatrick }
57061da546Spatrick 
ValueObjectVariable(ExecutionContextScope * exe_scope,ValueObjectManager & manager,const lldb::VariableSP & var_sp)58061da546Spatrick ValueObjectVariable::ValueObjectVariable(ExecutionContextScope *exe_scope,
59dda28197Spatrick                                          ValueObjectManager &manager,
60061da546Spatrick                                          const lldb::VariableSP &var_sp)
61dda28197Spatrick     : ValueObject(exe_scope, manager), m_variable_sp(var_sp) {
62061da546Spatrick   // Do not attempt to construct one of these objects with no variable!
63061da546Spatrick   assert(m_variable_sp.get() != nullptr);
64061da546Spatrick   m_name = var_sp->GetName();
65061da546Spatrick }
66061da546Spatrick 
67be691f3bSpatrick ValueObjectVariable::~ValueObjectVariable() = default;
68061da546Spatrick 
GetCompilerTypeImpl()69061da546Spatrick CompilerType ValueObjectVariable::GetCompilerTypeImpl() {
70061da546Spatrick   Type *var_type = m_variable_sp->GetType();
71061da546Spatrick   if (var_type)
72061da546Spatrick     return var_type->GetForwardCompilerType();
73061da546Spatrick   return CompilerType();
74061da546Spatrick }
75061da546Spatrick 
GetTypeName()76061da546Spatrick ConstString ValueObjectVariable::GetTypeName() {
77061da546Spatrick   Type *var_type = m_variable_sp->GetType();
78061da546Spatrick   if (var_type)
79061da546Spatrick     return var_type->GetName();
80061da546Spatrick   return ConstString();
81061da546Spatrick }
82061da546Spatrick 
GetDisplayTypeName()83061da546Spatrick ConstString ValueObjectVariable::GetDisplayTypeName() {
84061da546Spatrick   Type *var_type = m_variable_sp->GetType();
85061da546Spatrick   if (var_type)
86061da546Spatrick     return var_type->GetForwardCompilerType().GetDisplayTypeName();
87061da546Spatrick   return ConstString();
88061da546Spatrick }
89061da546Spatrick 
GetQualifiedTypeName()90061da546Spatrick ConstString ValueObjectVariable::GetQualifiedTypeName() {
91061da546Spatrick   Type *var_type = m_variable_sp->GetType();
92061da546Spatrick   if (var_type)
93061da546Spatrick     return var_type->GetQualifiedName();
94061da546Spatrick   return ConstString();
95061da546Spatrick }
96061da546Spatrick 
CalculateNumChildren(uint32_t max)97061da546Spatrick size_t ValueObjectVariable::CalculateNumChildren(uint32_t max) {
98061da546Spatrick   CompilerType type(GetCompilerType());
99061da546Spatrick 
100061da546Spatrick   if (!type.IsValid())
101061da546Spatrick     return 0;
102061da546Spatrick 
103061da546Spatrick   ExecutionContext exe_ctx(GetExecutionContextRef());
104061da546Spatrick   const bool omit_empty_base_classes = true;
105061da546Spatrick   auto child_count = type.GetNumChildren(omit_empty_base_classes, &exe_ctx);
106061da546Spatrick   return child_count <= max ? child_count : max;
107061da546Spatrick }
108061da546Spatrick 
GetByteSize()109*f6aab3d8Srobert std::optional<uint64_t> ValueObjectVariable::GetByteSize() {
110061da546Spatrick   ExecutionContext exe_ctx(GetExecutionContextRef());
111061da546Spatrick 
112061da546Spatrick   CompilerType type(GetCompilerType());
113061da546Spatrick 
114061da546Spatrick   if (!type.IsValid())
115be691f3bSpatrick     return {};
116061da546Spatrick 
117be691f3bSpatrick   return type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
118061da546Spatrick }
119061da546Spatrick 
GetValueType() const120061da546Spatrick lldb::ValueType ValueObjectVariable::GetValueType() const {
121061da546Spatrick   if (m_variable_sp)
122061da546Spatrick     return m_variable_sp->GetScope();
123061da546Spatrick   return lldb::eValueTypeInvalid;
124061da546Spatrick }
125061da546Spatrick 
UpdateValue()126061da546Spatrick bool ValueObjectVariable::UpdateValue() {
127061da546Spatrick   SetValueIsValid(false);
128061da546Spatrick   m_error.Clear();
129061da546Spatrick 
130061da546Spatrick   Variable *variable = m_variable_sp.get();
131*f6aab3d8Srobert   DWARFExpressionList &expr_list = variable->LocationExpressionList();
132061da546Spatrick 
133061da546Spatrick   if (variable->GetLocationIsConstantValueData()) {
134061da546Spatrick     // expr doesn't contain DWARF bytes, it contains the constant variable
135061da546Spatrick     // value bytes themselves...
136*f6aab3d8Srobert     if (expr_list.GetExpressionData(m_data)) {
137be691f3bSpatrick       if (m_data.GetDataStart() && m_data.GetByteSize())
138be691f3bSpatrick         m_value.SetBytes(m_data.GetDataStart(), m_data.GetByteSize());
139be691f3bSpatrick       m_value.SetContext(Value::ContextType::Variable, variable);
140*f6aab3d8Srobert     } else
141061da546Spatrick       m_error.SetErrorString("empty constant data");
142061da546Spatrick     // constant bytes can't be edited - sorry
143be691f3bSpatrick     m_resolved_value.SetContext(Value::ContextType::Invalid, nullptr);
144061da546Spatrick   } else {
145061da546Spatrick     lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS;
146061da546Spatrick     ExecutionContext exe_ctx(GetExecutionContextRef());
147061da546Spatrick 
148061da546Spatrick     Target *target = exe_ctx.GetTargetPtr();
149061da546Spatrick     if (target) {
150061da546Spatrick       m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
151061da546Spatrick       m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
152061da546Spatrick     }
153061da546Spatrick 
154*f6aab3d8Srobert     if (!expr_list.IsAlwaysValidSingleExpr()) {
155061da546Spatrick       SymbolContext sc;
156061da546Spatrick       variable->CalculateSymbolContext(&sc);
157061da546Spatrick       if (sc.function)
158061da546Spatrick         loclist_base_load_addr =
159061da546Spatrick             sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(
160061da546Spatrick                 target);
161061da546Spatrick     }
162061da546Spatrick     Value old_value(m_value);
163*f6aab3d8Srobert     if (expr_list.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr,
164061da546Spatrick                            nullptr, m_value, &m_error)) {
165061da546Spatrick       m_resolved_value = m_value;
166be691f3bSpatrick       m_value.SetContext(Value::ContextType::Variable, variable);
167061da546Spatrick 
168061da546Spatrick       CompilerType compiler_type = GetCompilerType();
169061da546Spatrick       if (compiler_type.IsValid())
170061da546Spatrick         m_value.SetCompilerType(compiler_type);
171061da546Spatrick 
172061da546Spatrick       Value::ValueType value_type = m_value.GetValueType();
173061da546Spatrick 
174dda28197Spatrick       // The size of the buffer within m_value can be less than the size
175dda28197Spatrick       // prescribed by its type. E.g. this can happen when an expression only
176dda28197Spatrick       // partially describes an object (say, because it contains DW_OP_piece).
177dda28197Spatrick       //
178dda28197Spatrick       // In this case, grow m_value to the expected size. An alternative way to
179dda28197Spatrick       // handle this is to teach Value::GetValueAsData() and ValueObjectChild
180dda28197Spatrick       // not to read past the end of a host buffer, but this gets impractically
181dda28197Spatrick       // complicated as a Value's host buffer may be shared with a distant
182dda28197Spatrick       // ancestor or sibling in the ValueObject hierarchy.
183dda28197Spatrick       //
184dda28197Spatrick       // FIXME: When we grow m_value, we should represent the added bits as
185dda28197Spatrick       // undefined somehow instead of as 0's.
186be691f3bSpatrick       if (value_type == Value::ValueType::HostAddress &&
187dda28197Spatrick           compiler_type.IsValid()) {
188dda28197Spatrick         if (size_t value_buf_size = m_value.GetBuffer().GetByteSize()) {
189dda28197Spatrick           size_t value_size = m_value.GetValueByteSize(&m_error, &exe_ctx);
190dda28197Spatrick           if (m_error.Success() && value_buf_size < value_size)
191dda28197Spatrick             m_value.ResizeData(value_size);
192dda28197Spatrick         }
193dda28197Spatrick       }
194dda28197Spatrick 
195061da546Spatrick       Process *process = exe_ctx.GetProcessPtr();
196061da546Spatrick       const bool process_is_alive = process && process->IsAlive();
197061da546Spatrick 
198061da546Spatrick       switch (value_type) {
199be691f3bSpatrick       case Value::ValueType::Invalid:
200be691f3bSpatrick         m_error.SetErrorString("invalid value");
201be691f3bSpatrick         break;
202be691f3bSpatrick       case Value::ValueType::Scalar:
203061da546Spatrick         // The variable value is in the Scalar value inside the m_value. We can
204061da546Spatrick         // point our m_data right to it.
205061da546Spatrick         m_error =
206061da546Spatrick             m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
207061da546Spatrick         break;
208061da546Spatrick 
209be691f3bSpatrick       case Value::ValueType::FileAddress:
210be691f3bSpatrick       case Value::ValueType::LoadAddress:
211be691f3bSpatrick       case Value::ValueType::HostAddress:
212061da546Spatrick         // The DWARF expression result was an address in the inferior process.
213061da546Spatrick         // If this variable is an aggregate type, we just need the address as
214061da546Spatrick         // the main value as all child variable objects will rely upon this
215061da546Spatrick         // location and add an offset and then read their own values as needed.
216061da546Spatrick         // If this variable is a simple type, we read all data for it into
217061da546Spatrick         // m_data. Make sure this type has a value before we try and read it
218061da546Spatrick 
219061da546Spatrick         // If we have a file address, convert it to a load address if we can.
220be691f3bSpatrick         if (value_type == Value::ValueType::FileAddress && process_is_alive)
221061da546Spatrick           m_value.ConvertToLoadAddress(GetModule().get(), target);
222061da546Spatrick 
223061da546Spatrick         if (!CanProvideValue()) {
224061da546Spatrick           // this value object represents an aggregate type whose children have
225061da546Spatrick           // values, but this object does not. So we say we are changed if our
226061da546Spatrick           // location has changed.
227061da546Spatrick           SetValueDidChange(value_type != old_value.GetValueType() ||
228061da546Spatrick                             m_value.GetScalar() != old_value.GetScalar());
229061da546Spatrick         } else {
230061da546Spatrick           // Copy the Value and set the context to use our Variable so it can
231061da546Spatrick           // extract read its value into m_data appropriately
232061da546Spatrick           Value value(m_value);
233be691f3bSpatrick           value.SetContext(Value::ContextType::Variable, variable);
234061da546Spatrick           m_error =
235061da546Spatrick               value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
236061da546Spatrick 
237061da546Spatrick           SetValueDidChange(value_type != old_value.GetValueType() ||
238061da546Spatrick                             m_value.GetScalar() != old_value.GetScalar());
239061da546Spatrick         }
240061da546Spatrick         break;
241061da546Spatrick       }
242061da546Spatrick 
243061da546Spatrick       SetValueIsValid(m_error.Success());
244061da546Spatrick     } else {
245061da546Spatrick       // could not find location, won't allow editing
246be691f3bSpatrick       m_resolved_value.SetContext(Value::ContextType::Invalid, nullptr);
247061da546Spatrick     }
248061da546Spatrick   }
249dda28197Spatrick 
250061da546Spatrick   return m_error.Success();
251061da546Spatrick }
252061da546Spatrick 
DoUpdateChildrenAddressType(ValueObject & valobj)253dda28197Spatrick void ValueObjectVariable::DoUpdateChildrenAddressType(ValueObject &valobj) {
254dda28197Spatrick   Value::ValueType value_type = valobj.GetValue().GetValueType();
255dda28197Spatrick   ExecutionContext exe_ctx(GetExecutionContextRef());
256dda28197Spatrick   Process *process = exe_ctx.GetProcessPtr();
257dda28197Spatrick   const bool process_is_alive = process && process->IsAlive();
258dda28197Spatrick   const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
259dda28197Spatrick   const bool is_pointer_or_ref =
260dda28197Spatrick       (type_info & (lldb::eTypeIsPointer | lldb::eTypeIsReference)) != 0;
261dda28197Spatrick 
262dda28197Spatrick   switch (value_type) {
263be691f3bSpatrick   case Value::ValueType::Invalid:
264be691f3bSpatrick     break;
265be691f3bSpatrick   case Value::ValueType::FileAddress:
266dda28197Spatrick     // If this type is a pointer, then its children will be considered load
267dda28197Spatrick     // addresses if the pointer or reference is dereferenced, but only if
268dda28197Spatrick     // the process is alive.
269dda28197Spatrick     //
270dda28197Spatrick     // There could be global variables like in the following code:
271dda28197Spatrick     // struct LinkedListNode { Foo* foo; LinkedListNode* next; };
272dda28197Spatrick     // Foo g_foo1;
273dda28197Spatrick     // Foo g_foo2;
274dda28197Spatrick     // LinkedListNode g_second_node = { &g_foo2, NULL };
275dda28197Spatrick     // LinkedListNode g_first_node = { &g_foo1, &g_second_node };
276dda28197Spatrick     //
277dda28197Spatrick     // When we aren't running, we should be able to look at these variables
278dda28197Spatrick     // using the "target variable" command. Children of the "g_first_node"
279dda28197Spatrick     // always will be of the same address type as the parent. But children
280dda28197Spatrick     // of the "next" member of LinkedListNode will become load addresses if
281dda28197Spatrick     // we have a live process, or remain a file address if it was a file
282dda28197Spatrick     // address.
283dda28197Spatrick     if (process_is_alive && is_pointer_or_ref)
284dda28197Spatrick       valobj.SetAddressTypeOfChildren(eAddressTypeLoad);
285dda28197Spatrick     else
286dda28197Spatrick       valobj.SetAddressTypeOfChildren(eAddressTypeFile);
287dda28197Spatrick     break;
288be691f3bSpatrick   case Value::ValueType::HostAddress:
289dda28197Spatrick     // Same as above for load addresses, except children of pointer or refs
290dda28197Spatrick     // are always load addresses. Host addresses are used to store freeze
291dda28197Spatrick     // dried variables. If this type is a struct, the entire struct
292dda28197Spatrick     // contents will be copied into the heap of the
293dda28197Spatrick     // LLDB process, but we do not currently follow any pointers.
294dda28197Spatrick     if (is_pointer_or_ref)
295dda28197Spatrick       valobj.SetAddressTypeOfChildren(eAddressTypeLoad);
296dda28197Spatrick     else
297dda28197Spatrick       valobj.SetAddressTypeOfChildren(eAddressTypeHost);
298dda28197Spatrick     break;
299be691f3bSpatrick   case Value::ValueType::LoadAddress:
300be691f3bSpatrick   case Value::ValueType::Scalar:
301dda28197Spatrick     valobj.SetAddressTypeOfChildren(eAddressTypeLoad);
302dda28197Spatrick     break;
303dda28197Spatrick   }
304dda28197Spatrick }
305dda28197Spatrick 
306dda28197Spatrick 
307dda28197Spatrick 
IsInScope()308061da546Spatrick bool ValueObjectVariable::IsInScope() {
309061da546Spatrick   const ExecutionContextRef &exe_ctx_ref = GetExecutionContextRef();
310061da546Spatrick   if (exe_ctx_ref.HasFrameRef()) {
311061da546Spatrick     ExecutionContext exe_ctx(exe_ctx_ref);
312061da546Spatrick     StackFrame *frame = exe_ctx.GetFramePtr();
313061da546Spatrick     if (frame) {
314061da546Spatrick       return m_variable_sp->IsInScope(frame);
315061da546Spatrick     } else {
316061da546Spatrick       // This ValueObject had a frame at one time, but now we can't locate it,
317061da546Spatrick       // so return false since we probably aren't in scope.
318061da546Spatrick       return false;
319061da546Spatrick     }
320061da546Spatrick   }
321061da546Spatrick   // We have a variable that wasn't tied to a frame, which means it is a global
322061da546Spatrick   // and is always in scope.
323061da546Spatrick   return true;
324061da546Spatrick }
325061da546Spatrick 
GetModule()326061da546Spatrick lldb::ModuleSP ValueObjectVariable::GetModule() {
327061da546Spatrick   if (m_variable_sp) {
328061da546Spatrick     SymbolContextScope *sc_scope = m_variable_sp->GetSymbolContextScope();
329061da546Spatrick     if (sc_scope) {
330061da546Spatrick       return sc_scope->CalculateSymbolContextModule();
331061da546Spatrick     }
332061da546Spatrick   }
333061da546Spatrick   return lldb::ModuleSP();
334061da546Spatrick }
335061da546Spatrick 
GetSymbolContextScope()336061da546Spatrick SymbolContextScope *ValueObjectVariable::GetSymbolContextScope() {
337061da546Spatrick   if (m_variable_sp)
338061da546Spatrick     return m_variable_sp->GetSymbolContextScope();
339061da546Spatrick   return nullptr;
340061da546Spatrick }
341061da546Spatrick 
GetDeclaration(Declaration & decl)342061da546Spatrick bool ValueObjectVariable::GetDeclaration(Declaration &decl) {
343061da546Spatrick   if (m_variable_sp) {
344061da546Spatrick     decl = m_variable_sp->GetDeclaration();
345061da546Spatrick     return true;
346061da546Spatrick   }
347061da546Spatrick   return false;
348061da546Spatrick }
349061da546Spatrick 
GetLocationAsCString()350061da546Spatrick const char *ValueObjectVariable::GetLocationAsCString() {
351be691f3bSpatrick   if (m_resolved_value.GetContextType() == Value::ContextType::RegisterInfo)
352061da546Spatrick     return GetLocationAsCStringImpl(m_resolved_value, m_data);
353061da546Spatrick   else
354061da546Spatrick     return ValueObject::GetLocationAsCString();
355061da546Spatrick }
356061da546Spatrick 
SetValueFromCString(const char * value_str,Status & error)357061da546Spatrick bool ValueObjectVariable::SetValueFromCString(const char *value_str,
358061da546Spatrick                                               Status &error) {
359061da546Spatrick   if (!UpdateValueIfNeeded()) {
360061da546Spatrick     error.SetErrorString("unable to update value before writing");
361061da546Spatrick     return false;
362061da546Spatrick   }
363061da546Spatrick 
364be691f3bSpatrick   if (m_resolved_value.GetContextType() == Value::ContextType::RegisterInfo) {
365061da546Spatrick     RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
366061da546Spatrick     ExecutionContext exe_ctx(GetExecutionContextRef());
367061da546Spatrick     RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
368061da546Spatrick     RegisterValue reg_value;
369061da546Spatrick     if (!reg_info || !reg_ctx) {
370061da546Spatrick       error.SetErrorString("unable to retrieve register info");
371061da546Spatrick       return false;
372061da546Spatrick     }
373061da546Spatrick     error = reg_value.SetValueFromString(reg_info, llvm::StringRef(value_str));
374061da546Spatrick     if (error.Fail())
375061da546Spatrick       return false;
376061da546Spatrick     if (reg_ctx->WriteRegister(reg_info, reg_value)) {
377061da546Spatrick       SetNeedsUpdate();
378061da546Spatrick       return true;
379061da546Spatrick     } else {
380061da546Spatrick       error.SetErrorString("unable to write back to register");
381061da546Spatrick       return false;
382061da546Spatrick     }
383061da546Spatrick   } else
384061da546Spatrick     return ValueObject::SetValueFromCString(value_str, error);
385061da546Spatrick }
386061da546Spatrick 
SetData(DataExtractor & data,Status & error)387061da546Spatrick bool ValueObjectVariable::SetData(DataExtractor &data, Status &error) {
388061da546Spatrick   if (!UpdateValueIfNeeded()) {
389061da546Spatrick     error.SetErrorString("unable to update value before writing");
390061da546Spatrick     return false;
391061da546Spatrick   }
392061da546Spatrick 
393be691f3bSpatrick   if (m_resolved_value.GetContextType() == Value::ContextType::RegisterInfo) {
394061da546Spatrick     RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
395061da546Spatrick     ExecutionContext exe_ctx(GetExecutionContextRef());
396061da546Spatrick     RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
397061da546Spatrick     RegisterValue reg_value;
398061da546Spatrick     if (!reg_info || !reg_ctx) {
399061da546Spatrick       error.SetErrorString("unable to retrieve register info");
400061da546Spatrick       return false;
401061da546Spatrick     }
402*f6aab3d8Srobert     error = reg_value.SetValueFromData(*reg_info, data, 0, true);
403061da546Spatrick     if (error.Fail())
404061da546Spatrick       return false;
405061da546Spatrick     if (reg_ctx->WriteRegister(reg_info, reg_value)) {
406061da546Spatrick       SetNeedsUpdate();
407061da546Spatrick       return true;
408061da546Spatrick     } else {
409061da546Spatrick       error.SetErrorString("unable to write back to register");
410061da546Spatrick       return false;
411061da546Spatrick     }
412061da546Spatrick   } else
413061da546Spatrick     return ValueObject::SetData(data, error);
414061da546Spatrick }
415