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