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
.SetErrorString(
124 "assign operation takes one or more key=value arguments");
127 for (const auto &entry
: args
) {
128 if (entry
.ref().empty()) {
129 error
.SetErrorString("empty argument");
132 if (!entry
.ref().contains('=')) {
133 error
.SetErrorString(
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
.SetErrorString("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
.SetErrorStringWithFormat(
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
.SetErrorString("dictionaries that can contain multiple types "
195 "must subclass OptionValueArray");
201 case eVarSetOperationRemove
:
203 for (size_t i
= 0; i
< argc
; ++i
) {
204 llvm::StringRef
key(args
.GetArgumentAtIndex(i
));
205 if (!DeleteValueForKey(key
)) {
206 error
.SetErrorStringWithFormat(
207 "no value found named '%s', aborting remove operation",
213 error
.SetErrorString("remove operation takes one or more key arguments");
217 case eVarSetOperationInsertBefore
:
218 case eVarSetOperationInsertAfter
:
219 case eVarSetOperationInvalid
:
220 error
= OptionValue::SetValueFromString(llvm::StringRef(), op
);
226 Status
OptionValueDictionary::SetValueFromString(llvm::StringRef value
,
227 VarSetOperationType op
) {
228 Args
args(value
.str());
229 Status error
= SetArgs(args
, op
);
231 NotifyValueChanged();
236 OptionValueDictionary::GetSubValue(const ExecutionContext
*exe_ctx
,
237 llvm::StringRef name
, Status
&error
) const {
238 lldb::OptionValueSP value_sp
;
242 llvm::StringRef left
, temp
;
243 std::tie(left
, temp
) = name
.split('[');
244 if (left
.size() == name
.size()) {
245 error
.SetErrorStringWithFormat("invalid value path '%s', %s values only "
246 "support '[<key>]' subvalues where <key> "
247 "a string value optionally delimited by "
248 "single or double quotes",
249 name
.str().c_str(), GetTypeAsCString());
252 assert(!temp
.empty());
254 llvm::StringRef key
, quote_char
;
256 if (temp
[0] == '\"' || temp
[0] == '\'') {
257 quote_char
= temp
.take_front();
258 temp
= temp
.drop_front();
261 llvm::StringRef sub_name
;
262 std::tie(key
, sub_name
) = temp
.split(']');
264 if (!key
.consume_back(quote_char
) || key
.empty()) {
265 error
.SetErrorStringWithFormat("invalid value path '%s', "
266 "key names must be formatted as ['<key>'] where <key> "
267 "is a string that doesn't contain quotes and the quote"
268 " char is optional", name
.str().c_str());
272 value_sp
= GetValueForKey(key
);
274 error
.SetErrorStringWithFormat(
275 "dictionary does not contain a value for the key name '%s'",
280 if (sub_name
.empty())
282 return value_sp
->GetSubValue(exe_ctx
, sub_name
, error
);
285 Status
OptionValueDictionary::SetSubValue(const ExecutionContext
*exe_ctx
,
286 VarSetOperationType op
,
287 llvm::StringRef name
,
288 llvm::StringRef value
) {
290 lldb::OptionValueSP
value_sp(GetSubValue(exe_ctx
, name
, error
));
292 error
= value_sp
->SetValueFromString(value
, op
);
294 if (error
.AsCString() == nullptr)
295 error
.SetErrorStringWithFormat("invalid value path '%s'", name
.str().c_str());
301 OptionValueDictionary::GetValueForKey(llvm::StringRef key
) const {
302 lldb::OptionValueSP value_sp
;
303 auto pos
= m_values
.find(key
);
304 if (pos
!= m_values
.end())
305 value_sp
= pos
->second
;
309 bool OptionValueDictionary::SetValueForKey(llvm::StringRef key
,
310 const lldb::OptionValueSP
&value_sp
,
312 // Make sure the value_sp object is allowed to contain values of the type
314 if (value_sp
&& (m_type_mask
& value_sp
->GetTypeAsMask())) {
316 auto pos
= m_values
.find(key
);
317 if (pos
!= m_values
.end())
320 m_values
[key
] = value_sp
;
326 bool OptionValueDictionary::DeleteValueForKey(llvm::StringRef key
) {
327 auto pos
= m_values
.find(key
);
328 if (pos
!= m_values
.end()) {
336 OptionValueDictionary::DeepCopy(const OptionValueSP
&new_parent
) const {
337 auto copy_sp
= OptionValue::DeepCopy(new_parent
);
338 // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived
339 // types that override GetType returning a different value.
340 auto *dict_value_ptr
= static_cast<OptionValueDictionary
*>(copy_sp
.get());
341 lldbassert(dict_value_ptr
);
343 for (auto &value
: dict_value_ptr
->m_values
)
344 value
.second
= value
.second
->DeepCopy(copy_sp
);