1 //===-- OptionValueArray.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/OptionValueArray.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Stream.h"
17 #include "lldb/Host/StringConvert.h"
18 #include "lldb/Interpreter/Args.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
24                                  uint32_t dump_mask) {
25   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
26   if (dump_mask & eDumpOptionType) {
27     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
28       strm.Printf("(%s of %ss)", GetTypeAsCString(),
29                   GetBuiltinTypeAsCString(array_element_type));
30     else
31       strm.Printf("(%s)", GetTypeAsCString());
32   }
33   if (dump_mask & eDumpOptionValue) {
34     if (dump_mask & eDumpOptionType)
35       strm.Printf(" =%s", (m_values.size() > 0) ? "\n" : "");
36     strm.IndentMore();
37     const uint32_t size = m_values.size();
38     for (uint32_t i = 0; i < size; ++i) {
39       strm.Indent();
40       strm.Printf("[%u]: ", i);
41       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
42       switch (array_element_type) {
43       default:
44       case eTypeArray:
45       case eTypeDictionary:
46       case eTypeProperties:
47       case eTypeFileSpecList:
48       case eTypePathMap:
49         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
50         break;
51 
52       case eTypeBoolean:
53       case eTypeChar:
54       case eTypeEnum:
55       case eTypeFileSpec:
56       case eTypeFormat:
57       case eTypeSInt64:
58       case eTypeString:
59       case eTypeUInt64:
60       case eTypeUUID:
61         // No need to show the type for dictionaries of simple items
62         m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
63                                                   extra_dump_options);
64         break;
65       }
66       if (i < (size - 1))
67         strm.EOL();
68     }
69     strm.IndentLess();
70   }
71 }
72 
73 Error OptionValueArray::SetValueFromString(llvm::StringRef value,
74                                            VarSetOperationType op) {
75   Args args(value.str().c_str());
76   Error error = SetArgs(args, op);
77   if (error.Success())
78     NotifyValueChanged();
79   return error;
80 }
81 
82 lldb::OptionValueSP
83 OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx, const char *name,
84                               bool will_modify, Error &error) const {
85   if (name && name[0] == '[') {
86     const char *end_bracket = strchr(name + 1, ']');
87     if (end_bracket) {
88       const char *sub_value = nullptr;
89       if (end_bracket[1])
90         sub_value = end_bracket + 1;
91       std::string index_str(name + 1, end_bracket);
92       const size_t array_count = m_values.size();
93       int32_t idx =
94           StringConvert::ToSInt32(index_str.c_str(), INT32_MAX, 0, nullptr);
95       if (idx != INT32_MAX) {
96         ;
97         uint32_t new_idx = UINT32_MAX;
98         if (idx < 0) {
99           // Access from the end of the array if the index is negative
100           new_idx = array_count - idx;
101         } else {
102           // Just a standard index
103           new_idx = idx;
104         }
105 
106         if (new_idx < array_count) {
107           if (m_values[new_idx]) {
108             if (sub_value)
109               return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
110                                                     will_modify, error);
111             else
112               return m_values[new_idx];
113           }
114         } else {
115           if (array_count == 0)
116             error.SetErrorStringWithFormat(
117                 "index %i is not valid for an empty array", idx);
118           else if (idx > 0)
119             error.SetErrorStringWithFormat(
120                 "index %i out of range, valid values are 0 through %" PRIu64,
121                 idx, (uint64_t)(array_count - 1));
122           else
123             error.SetErrorStringWithFormat("negative index %i out of range, "
124                                            "valid values are -1 through "
125                                            "-%" PRIu64,
126                                            idx, (uint64_t)array_count);
127         }
128       }
129     }
130   } else {
131     error.SetErrorStringWithFormat(
132         "invalid value path '%s', %s values only support '[<index>]' subvalues "
133         "where <index> is a positive or negative array index",
134         name, GetTypeAsCString());
135   }
136   return OptionValueSP();
137 }
138 
139 size_t OptionValueArray::GetArgs(Args &args) const {
140   const uint32_t size = m_values.size();
141   std::vector<const char *> argv;
142   for (uint32_t i = 0; i < size; ++i) {
143     const char *string_value = m_values[i]->GetStringValue();
144     if (string_value)
145       argv.push_back(string_value);
146   }
147 
148   if (argv.empty())
149     args.Clear();
150   else
151     args.SetArguments(argv.size(), &argv[0]);
152   return args.GetArgumentCount();
153 }
154 
155 Error OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
156   Error error;
157   const size_t argc = args.GetArgumentCount();
158   switch (op) {
159   case eVarSetOperationInvalid:
160     error.SetErrorString("unsupported operation");
161     break;
162 
163   case eVarSetOperationInsertBefore:
164   case eVarSetOperationInsertAfter:
165     if (argc > 1) {
166       uint32_t idx =
167           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
168       const uint32_t count = GetSize();
169       if (idx > count) {
170         error.SetErrorStringWithFormat(
171             "invalid insert array index %u, index must be 0 through %u", idx,
172             count);
173       } else {
174         if (op == eVarSetOperationInsertAfter)
175           ++idx;
176         for (size_t i = 1; i < argc; ++i, ++idx) {
177           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
178               args.GetArgumentAtIndex(i), m_type_mask, error));
179           if (value_sp) {
180             if (error.Fail())
181               return error;
182             if (idx >= m_values.size())
183               m_values.push_back(value_sp);
184             else
185               m_values.insert(m_values.begin() + idx, value_sp);
186           } else {
187             error.SetErrorString(
188                 "array of complex types must subclass OptionValueArray");
189             return error;
190           }
191         }
192       }
193     } else {
194       error.SetErrorString("insert operation takes an array index followed by "
195                            "one or more values");
196     }
197     break;
198 
199   case eVarSetOperationRemove:
200     if (argc > 0) {
201       const uint32_t size = m_values.size();
202       std::vector<int> remove_indexes;
203       bool all_indexes_valid = true;
204       size_t i;
205       for (i = 0; i < argc; ++i) {
206         const size_t idx =
207             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
208         if (idx >= size) {
209           all_indexes_valid = false;
210           break;
211         } else
212           remove_indexes.push_back(idx);
213       }
214 
215       if (all_indexes_valid) {
216         size_t num_remove_indexes = remove_indexes.size();
217         if (num_remove_indexes) {
218           // Sort and then erase in reverse so indexes are always valid
219           if (num_remove_indexes > 1) {
220             std::sort(remove_indexes.begin(), remove_indexes.end());
221             for (std::vector<int>::const_reverse_iterator
222                      pos = remove_indexes.rbegin(),
223                      end = remove_indexes.rend();
224                  pos != end; ++pos) {
225               m_values.erase(m_values.begin() + *pos);
226             }
227           } else {
228             // Only one index
229             m_values.erase(m_values.begin() + remove_indexes.front());
230           }
231         }
232       } else {
233         error.SetErrorStringWithFormat(
234             "invalid array index '%s', aborting remove operation",
235             args.GetArgumentAtIndex(i));
236       }
237     } else {
238       error.SetErrorString("remove operation takes one or more array indices");
239     }
240     break;
241 
242   case eVarSetOperationClear:
243     Clear();
244     break;
245 
246   case eVarSetOperationReplace:
247     if (argc > 1) {
248       uint32_t idx =
249           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
250       const uint32_t count = GetSize();
251       if (idx > count) {
252         error.SetErrorStringWithFormat(
253             "invalid replace array index %u, index must be 0 through %u", idx,
254             count);
255       } else {
256         for (size_t i = 1; i < argc; ++i, ++idx) {
257           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
258               args.GetArgumentAtIndex(i), m_type_mask, error));
259           if (value_sp) {
260             if (error.Fail())
261               return error;
262             if (idx < count)
263               m_values[idx] = value_sp;
264             else
265               m_values.push_back(value_sp);
266           } else {
267             error.SetErrorString(
268                 "array of complex types must subclass OptionValueArray");
269             return error;
270           }
271         }
272       }
273     } else {
274       error.SetErrorString("replace operation takes an array index followed by "
275                            "one or more values");
276     }
277     break;
278 
279   case eVarSetOperationAssign:
280     m_values.clear();
281     // Fall through to append case
282     LLVM_FALLTHROUGH;
283   case eVarSetOperationAppend:
284     for (size_t i = 0; i < argc; ++i) {
285       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
286           args.GetArgumentAtIndex(i), m_type_mask, error));
287       if (value_sp) {
288         if (error.Fail())
289           return error;
290         m_value_was_set = true;
291         AppendValue(value_sp);
292       } else {
293         error.SetErrorString(
294             "array of complex types must subclass OptionValueArray");
295       }
296     }
297     break;
298   }
299   return error;
300 }
301 
302 lldb::OptionValueSP OptionValueArray::DeepCopy() const {
303   OptionValueArray *copied_array =
304       new OptionValueArray(m_type_mask, m_raw_value_dump);
305   lldb::OptionValueSP copied_value_sp(copied_array);
306   *static_cast<OptionValue *>(copied_array) = *this;
307   copied_array->m_callback = m_callback;
308   const uint32_t size = m_values.size();
309   for (uint32_t i = 0; i < size; ++i) {
310     copied_array->AppendValue(m_values[i]->DeepCopy());
311   }
312   return copied_value_sp;
313 }
314