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