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