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