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