1 //===-- OptionValueDictionary.cpp -----------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Interpreter/OptionValueDictionary.h"
11 #include "lldb/DataFormatters/FormatManager.h"
12 #include "lldb/Interpreter/OptionValueEnumeration.h"
13 #include "lldb/Interpreter/OptionValueString.h"
14 #include "lldb/Utility/Args.h"
15 #include "lldb/Utility/State.h"
16 #include "llvm/ADT/StringRef.h"
19 using namespace lldb_private
;
21 void OptionValueDictionary::DumpValue(const ExecutionContext
*exe_ctx
,
22 Stream
&strm
, uint32_t dump_mask
) {
23 const Type dict_type
= ConvertTypeMaskToType(m_type_mask
);
24 if (dump_mask
& eDumpOptionType
) {
25 if (m_type_mask
!= eTypeInvalid
)
26 strm
.Printf("(%s of %ss)", GetTypeAsCString(),
27 GetBuiltinTypeAsCString(dict_type
));
29 strm
.Printf("(%s)", GetTypeAsCString());
31 if (dump_mask
& eDumpOptionValue
) {
32 const bool one_line
= dump_mask
& eDumpOptionCommand
;
33 if (dump_mask
& eDumpOptionType
)
34 strm
.PutCString(" =");
39 // m_values is not guaranteed to be sorted alphabetically, so for
40 // consistentcy we will sort them here before dumping
41 std::map
<llvm::StringRef
, OptionValue
*> sorted_values
;
42 for (const auto &value
: m_values
) {
43 sorted_values
[value
.first()] = value
.second
.get();
45 for (const auto &value
: sorted_values
) {
46 OptionValue
*option_value
= value
.second
;
53 strm
.Indent(value
.first
);
55 const uint32_t extra_dump_options
= m_raw_value_dump
? eDumpOptionRaw
: 0;
61 case eTypeFileSpecList
:
64 option_value
->DumpValue(exe_ctx
, strm
, dump_mask
| extra_dump_options
);
70 case eTypeFileLineColumn
:
77 // No need to show the type for dictionaries of simple items
79 option_value
->DumpValue(exe_ctx
, strm
,
80 (dump_mask
& (~eDumpOptionType
)) |
91 OptionValueDictionary::ToJSON(const ExecutionContext
*exe_ctx
) {
92 llvm::json::Object dict
;
93 for (const auto &value
: m_values
) {
94 dict
.try_emplace(value
.first(), value
.second
->ToJSON(exe_ctx
));
99 size_t OptionValueDictionary::GetArgs(Args
&args
) const {
101 for (const auto &value
: m_values
) {
103 strm
.Printf("%s=", value
.first().data());
104 value
.second
->DumpValue(nullptr, strm
, eDumpOptionValue
| eDumpOptionRaw
);
105 args
.AppendArgument(strm
.GetString());
107 return args
.GetArgumentCount();
110 Status
OptionValueDictionary::SetArgs(const Args
&args
,
111 VarSetOperationType op
) {
113 const size_t argc
= args
.GetArgumentCount();
115 case eVarSetOperationClear
:
119 case eVarSetOperationAppend
:
120 case eVarSetOperationReplace
:
121 case eVarSetOperationAssign
:
123 error
= Status::FromErrorString(
124 "assign operation takes one or more key=value arguments");
127 for (const auto &entry
: args
) {
128 if (entry
.ref().empty()) {
129 error
= Status::FromErrorString("empty argument");
132 if (!entry
.ref().contains('=')) {
133 error
= Status::FromErrorString(
134 "assign operation takes one or more key=value arguments");
138 llvm::StringRef key
, value
;
139 std::tie(key
, value
) = entry
.ref().split('=');
140 bool key_valid
= false;
142 error
= Status::FromErrorString("empty dictionary key");
146 if (key
.front() == '[') {
147 // Key name starts with '[', so the key value must be in single or
148 // double quotes like: ['<key>'] ["<key>"]
149 if ((key
.size() > 2) && (key
.back() == ']')) {
150 // Strip leading '[' and trailing ']'
151 key
= key
.substr(1, key
.size() - 2);
152 const char quote_char
= key
.front();
153 if ((quote_char
== '\'') || (quote_char
== '"')) {
154 if ((key
.size() > 2) && (key
.back() == quote_char
)) {
156 key
= key
.substr(1, key
.size() - 2);
160 // square brackets, no quotes
165 // No square brackets or quotes
169 error
= Status::FromErrorStringWithFormat(
170 "invalid key \"%s\", the key must be a bare string or "
171 "surrounded by brackets with optional quotes: [<key>] or "
172 "['<key>'] or [\"<key>\"]",
177 if (m_type_mask
== 1u << eTypeEnum
) {
179 std::make_shared
<OptionValueEnumeration
>(m_enum_values
, 0);
180 error
= enum_value
->SetValueFromString(value
);
183 m_value_was_set
= true;
184 SetValueForKey(key
, enum_value
, true);
186 lldb::OptionValueSP
value_sp(CreateValueFromCStringForTypeMask(
187 value
.str().c_str(), m_type_mask
, error
));
191 m_value_was_set
= true;
192 SetValueForKey(key
, value_sp
, true);
194 error
= Status::FromErrorString(
195 "dictionaries that can contain multiple types "
196 "must subclass OptionValueArray");
202 case eVarSetOperationRemove
:
204 for (size_t i
= 0; i
< argc
; ++i
) {
205 llvm::StringRef
key(args
.GetArgumentAtIndex(i
));
206 if (!DeleteValueForKey(key
)) {
207 error
= Status::FromErrorStringWithFormat(
208 "no value found named '%s', aborting remove operation",
214 error
= Status::FromErrorString(
215 "remove operation takes one or more key arguments");
219 case eVarSetOperationInsertBefore
:
220 case eVarSetOperationInsertAfter
:
221 case eVarSetOperationInvalid
:
222 error
= OptionValue::SetValueFromString(llvm::StringRef(), op
);
228 Status
OptionValueDictionary::SetValueFromString(llvm::StringRef value
,
229 VarSetOperationType op
) {
230 Args
args(value
.str());
231 Status error
= SetArgs(args
, op
);
233 NotifyValueChanged();
238 OptionValueDictionary::GetSubValue(const ExecutionContext
*exe_ctx
,
239 llvm::StringRef name
, Status
&error
) const {
240 lldb::OptionValueSP value_sp
;
244 llvm::StringRef left
, temp
;
245 std::tie(left
, temp
) = name
.split('[');
246 if (left
.size() == name
.size()) {
247 error
= Status::FromErrorStringWithFormat(
248 "invalid value path '%s', %s values only "
249 "support '[<key>]' subvalues where <key> "
250 "a string value optionally delimited by "
251 "single or double quotes",
252 name
.str().c_str(), GetTypeAsCString());
255 assert(!temp
.empty());
257 llvm::StringRef key
, quote_char
;
259 if (temp
[0] == '\"' || temp
[0] == '\'') {
260 quote_char
= temp
.take_front();
261 temp
= temp
.drop_front();
264 llvm::StringRef sub_name
;
265 std::tie(key
, sub_name
) = temp
.split(']');
267 if (!key
.consume_back(quote_char
) || key
.empty()) {
268 error
= Status::FromErrorStringWithFormat(
269 "invalid value path '%s', "
270 "key names must be formatted as ['<key>'] where <key> "
271 "is a string that doesn't contain quotes and the quote"
277 value_sp
= GetValueForKey(key
);
279 error
= Status::FromErrorStringWithFormat(
280 "dictionary does not contain a value for the key name '%s'",
285 if (sub_name
.empty())
287 return value_sp
->GetSubValue(exe_ctx
, sub_name
, error
);
290 Status
OptionValueDictionary::SetSubValue(const ExecutionContext
*exe_ctx
,
291 VarSetOperationType op
,
292 llvm::StringRef name
,
293 llvm::StringRef value
) {
295 lldb::OptionValueSP
value_sp(GetSubValue(exe_ctx
, name
, error
));
297 error
= value_sp
->SetValueFromString(value
, op
);
299 if (error
.AsCString() == nullptr)
300 error
= Status::FromErrorStringWithFormat("invalid value path '%s'",
307 OptionValueDictionary::GetValueForKey(llvm::StringRef key
) const {
308 lldb::OptionValueSP value_sp
;
309 auto pos
= m_values
.find(key
);
310 if (pos
!= m_values
.end())
311 value_sp
= pos
->second
;
315 bool OptionValueDictionary::SetValueForKey(llvm::StringRef key
,
316 const lldb::OptionValueSP
&value_sp
,
318 // Make sure the value_sp object is allowed to contain values of the type
320 if (value_sp
&& (m_type_mask
& value_sp
->GetTypeAsMask())) {
322 auto pos
= m_values
.find(key
);
323 if (pos
!= m_values
.end())
326 m_values
[key
] = value_sp
;
332 bool OptionValueDictionary::DeleteValueForKey(llvm::StringRef key
) {
333 auto pos
= m_values
.find(key
);
334 if (pos
!= m_values
.end()) {
342 OptionValueDictionary::DeepCopy(const OptionValueSP
&new_parent
) const {
343 auto copy_sp
= OptionValue::DeepCopy(new_parent
);
344 // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived
345 // types that override GetType returning a different value.
346 auto *dict_value_ptr
= static_cast<OptionValueDictionary
*>(copy_sp
.get());
347 lldbassert(dict_value_ptr
);
349 for (auto &value
: dict_value_ptr
->m_values
)
350 value
.second
= value
.second
->DeepCopy(copy_sp
);