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