1 //===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Interpreter/OptionValueDictionary.h"
10
11 #include "llvm/ADT/StringRef.h"
12 #include "lldb/DataFormatters/FormatManager.h"
13 #include "lldb/Interpreter/OptionValueString.h"
14 #include "lldb/Utility/Args.h"
15 #include "lldb/Utility/State.h"
16
17 using namespace lldb;
18 using namespace lldb_private;
19
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)20 void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
21 Stream &strm, uint32_t dump_mask) {
22 const Type dict_type = ConvertTypeMaskToType(m_type_mask);
23 if (dump_mask & eDumpOptionType) {
24 if (m_type_mask != eTypeInvalid)
25 strm.Printf("(%s of %ss)", GetTypeAsCString(),
26 GetBuiltinTypeAsCString(dict_type));
27 else
28 strm.Printf("(%s)", GetTypeAsCString());
29 }
30 if (dump_mask & eDumpOptionValue) {
31 const bool one_line = dump_mask & eDumpOptionCommand;
32 if (dump_mask & eDumpOptionType)
33 strm.PutCString(" =");
34
35 collection::iterator pos, end = m_values.end();
36
37 if (!one_line)
38 strm.IndentMore();
39
40 for (pos = m_values.begin(); pos != end; ++pos) {
41 OptionValue *option_value = pos->second.get();
42
43 if (one_line)
44 strm << ' ';
45 else
46 strm.EOL();
47
48 strm.Indent(pos->first.GetCString());
49
50 const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
51 switch (dict_type) {
52 default:
53 case eTypeArray:
54 case eTypeDictionary:
55 case eTypeProperties:
56 case eTypeFileSpecList:
57 case eTypePathMap:
58 strm.PutChar(' ');
59 option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
60 break;
61
62 case eTypeBoolean:
63 case eTypeChar:
64 case eTypeEnum:
65 case eTypeFileSpec:
66 case eTypeFormat:
67 case eTypeSInt64:
68 case eTypeString:
69 case eTypeUInt64:
70 case eTypeUUID:
71 // No need to show the type for dictionaries of simple items
72 strm.PutCString("=");
73 option_value->DumpValue(exe_ctx, strm,
74 (dump_mask & (~eDumpOptionType)) |
75 extra_dump_options);
76 break;
77 }
78 }
79 if (!one_line)
80 strm.IndentLess();
81 }
82 }
83
GetArgs(Args & args) const84 size_t OptionValueDictionary::GetArgs(Args &args) const {
85 args.Clear();
86 collection::const_iterator pos, end = m_values.end();
87 for (pos = m_values.begin(); pos != end; ++pos) {
88 StreamString strm;
89 strm.Printf("%s=", pos->first.GetCString());
90 pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
91 args.AppendArgument(strm.GetString());
92 }
93 return args.GetArgumentCount();
94 }
95
SetArgs(const Args & args,VarSetOperationType op)96 Status OptionValueDictionary::SetArgs(const Args &args,
97 VarSetOperationType op) {
98 Status error;
99 const size_t argc = args.GetArgumentCount();
100 switch (op) {
101 case eVarSetOperationClear:
102 Clear();
103 break;
104
105 case eVarSetOperationAppend:
106 case eVarSetOperationReplace:
107 case eVarSetOperationAssign:
108 if (argc == 0) {
109 error.SetErrorString(
110 "assign operation takes one or more key=value arguments");
111 return error;
112 }
113 for (const auto &entry : args) {
114 if (entry.ref().empty()) {
115 error.SetErrorString("empty argument");
116 return error;
117 }
118 if (!entry.ref().contains('=')) {
119 error.SetErrorString(
120 "assign operation takes one or more key=value arguments");
121 return error;
122 }
123
124 llvm::StringRef key, value;
125 std::tie(key, value) = entry.ref().split('=');
126 bool key_valid = false;
127 if (key.empty()) {
128 error.SetErrorString("empty dictionary key");
129 return error;
130 }
131
132 if (key.front() == '[') {
133 // Key name starts with '[', so the key value must be in single or
134 // double quotes like: ['<key>'] ["<key>"]
135 if ((key.size() > 2) && (key.back() == ']')) {
136 // Strip leading '[' and trailing ']'
137 key = key.substr(1, key.size() - 2);
138 const char quote_char = key.front();
139 if ((quote_char == '\'') || (quote_char == '"')) {
140 if ((key.size() > 2) && (key.back() == quote_char)) {
141 // Strip the quotes
142 key = key.substr(1, key.size() - 2);
143 key_valid = true;
144 }
145 } else {
146 // square brackets, no quotes
147 key_valid = true;
148 }
149 }
150 } else {
151 // No square brackets or quotes
152 key_valid = true;
153 }
154 if (!key_valid) {
155 error.SetErrorStringWithFormat(
156 "invalid key \"%s\", the key must be a bare string or "
157 "surrounded by brackets with optional quotes: [<key>] or "
158 "['<key>'] or [\"<key>\"]",
159 key.str().c_str());
160 return error;
161 }
162
163 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
164 value.str().c_str(), m_type_mask, error));
165 if (value_sp) {
166 if (error.Fail())
167 return error;
168 m_value_was_set = true;
169 SetValueForKey(ConstString(key), value_sp, true);
170 } else {
171 error.SetErrorString("dictionaries that can contain multiple types "
172 "must subclass OptionValueArray");
173 }
174 }
175 break;
176
177 case eVarSetOperationRemove:
178 if (argc > 0) {
179 for (size_t i = 0; i < argc; ++i) {
180 ConstString key(args.GetArgumentAtIndex(i));
181 if (!DeleteValueForKey(key)) {
182 error.SetErrorStringWithFormat(
183 "no value found named '%s', aborting remove operation",
184 key.GetCString());
185 break;
186 }
187 }
188 } else {
189 error.SetErrorString("remove operation takes one or more key arguments");
190 }
191 break;
192
193 case eVarSetOperationInsertBefore:
194 case eVarSetOperationInsertAfter:
195 case eVarSetOperationInvalid:
196 error = OptionValue::SetValueFromString(llvm::StringRef(), op);
197 break;
198 }
199 return error;
200 }
201
SetValueFromString(llvm::StringRef value,VarSetOperationType op)202 Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
203 VarSetOperationType op) {
204 Args args(value.str());
205 Status error = SetArgs(args, op);
206 if (error.Success())
207 NotifyValueChanged();
208 return error;
209 }
210
211 lldb::OptionValueSP
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,bool will_modify,Status & error) const212 OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
213 llvm::StringRef name, bool will_modify,
214 Status &error) const {
215 lldb::OptionValueSP value_sp;
216 if (name.empty())
217 return nullptr;
218
219 llvm::StringRef left, temp;
220 std::tie(left, temp) = name.split('[');
221 if (left.size() == name.size()) {
222 error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
223 "support '[<key>]' subvalues where <key> "
224 "a string value optionally delimited by "
225 "single or double quotes",
226 name.str().c_str(), GetTypeAsCString());
227 return nullptr;
228 }
229 assert(!temp.empty());
230
231 llvm::StringRef key, quote_char;
232
233 if (temp[0] == '\"' || temp[0] == '\'') {
234 quote_char = temp.take_front();
235 temp = temp.drop_front();
236 }
237
238 llvm::StringRef sub_name;
239 std::tie(key, sub_name) = temp.split(']');
240
241 if (!key.consume_back(quote_char) || key.empty()) {
242 error.SetErrorStringWithFormat("invalid value path '%s', "
243 "key names must be formatted as ['<key>'] where <key> "
244 "is a string that doesn't contain quotes and the quote"
245 " char is optional", name.str().c_str());
246 return nullptr;
247 }
248
249 value_sp = GetValueForKey(ConstString(key));
250 if (!value_sp) {
251 error.SetErrorStringWithFormat(
252 "dictionary does not contain a value for the key name '%s'",
253 key.str().c_str());
254 return nullptr;
255 }
256
257 if (sub_name.empty())
258 return value_sp;
259 return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
260 }
261
SetSubValue(const ExecutionContext * exe_ctx,VarSetOperationType op,llvm::StringRef name,llvm::StringRef value)262 Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
263 VarSetOperationType op,
264 llvm::StringRef name,
265 llvm::StringRef value) {
266 Status error;
267 const bool will_modify = true;
268 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
269 if (value_sp)
270 error = value_sp->SetValueFromString(value, op);
271 else {
272 if (error.AsCString() == nullptr)
273 error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
274 }
275 return error;
276 }
277
278 lldb::OptionValueSP
GetValueForKey(ConstString key) const279 OptionValueDictionary::GetValueForKey(ConstString key) const {
280 lldb::OptionValueSP value_sp;
281 collection::const_iterator pos = m_values.find(key);
282 if (pos != m_values.end())
283 value_sp = pos->second;
284 return value_sp;
285 }
286
SetValueForKey(ConstString key,const lldb::OptionValueSP & value_sp,bool can_replace)287 bool OptionValueDictionary::SetValueForKey(ConstString key,
288 const lldb::OptionValueSP &value_sp,
289 bool can_replace) {
290 // Make sure the value_sp object is allowed to contain values of the type
291 // passed in...
292 if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
293 if (!can_replace) {
294 collection::const_iterator pos = m_values.find(key);
295 if (pos != m_values.end())
296 return false;
297 }
298 m_values[key] = value_sp;
299 return true;
300 }
301 return false;
302 }
303
DeleteValueForKey(ConstString key)304 bool OptionValueDictionary::DeleteValueForKey(ConstString key) {
305 collection::iterator pos = m_values.find(key);
306 if (pos != m_values.end()) {
307 m_values.erase(pos);
308 return true;
309 }
310 return false;
311 }
312
DeepCopy() const313 lldb::OptionValueSP OptionValueDictionary::DeepCopy() const {
314 OptionValueDictionary *copied_dict =
315 new OptionValueDictionary(m_type_mask, m_raw_value_dump);
316 lldb::OptionValueSP copied_value_sp(copied_dict);
317 collection::const_iterator pos, end = m_values.end();
318 for (pos = m_values.begin(); pos != end; ++pos) {
319 StreamString strm;
320 strm.Printf("%s=", pos->first.GetCString());
321 copied_dict->SetValueForKey(pos->first, pos->second->DeepCopy(), true);
322 }
323 return copied_value_sp;
324 }
325