1 //===-- OptionValueArray.cpp ----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Interpreter/OptionValueArray.h"
10 
11 #include "lldb/Utility/Args.h"
12 #include "lldb/Utility/Stream.h"
13 
14 using namespace lldb;
15 using namespace lldb_private;
16 
17 void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
18                                  uint32_t dump_mask) {
19   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
20   if (dump_mask & eDumpOptionType) {
21     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
22       strm.Printf("(%s of %ss)", GetTypeAsCString(),
23                   GetBuiltinTypeAsCString(array_element_type));
24     else
25       strm.Printf("(%s)", GetTypeAsCString());
26   }
27   if (dump_mask & eDumpOptionValue) {
28     const bool one_line = dump_mask & eDumpOptionCommand;
29     const uint32_t size = m_values.size();
30     if (dump_mask & eDumpOptionType)
31       strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
32     if (!one_line)
33       strm.IndentMore();
34     for (uint32_t i = 0; i < size; ++i) {
35       if (!one_line) {
36         strm.Indent();
37         strm.Printf("[%u]: ", i);
38       }
39       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
40       switch (array_element_type) {
41       default:
42       case eTypeArray:
43       case eTypeDictionary:
44       case eTypeProperties:
45       case eTypeFileSpecList:
46       case eTypePathMap:
47         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
48         break;
49 
50       case eTypeBoolean:
51       case eTypeChar:
52       case eTypeEnum:
53       case eTypeFileSpec:
54       case eTypeFileLineColumn:
55       case eTypeFormat:
56       case eTypeSInt64:
57       case eTypeString:
58       case eTypeUInt64:
59       case eTypeUUID:
60         // No need to show the type for dictionaries of simple items
61         m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
62                                                   extra_dump_options);
63         break;
64       }
65 
66       if (!one_line) {
67         if (i < (size - 1))
68           strm.EOL();
69       } else {
70         strm << ' ';
71       }
72     }
73     if (!one_line)
74       strm.IndentLess();
75   }
76 }
77 
78 Status OptionValueArray::SetValueFromString(llvm::StringRef value,
79                                             VarSetOperationType op) {
80   Args args(value.str());
81   Status error = SetArgs(args, op);
82   if (error.Success())
83     NotifyValueChanged();
84   return error;
85 }
86 
87 lldb::OptionValueSP
88 OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
89                               llvm::StringRef name, bool will_modify,
90                               Status &error) const {
91   if (name.empty() || name.front() != '[') {
92     error.SetErrorStringWithFormat(
93       "invalid value path '%s', %s values only support '[<index>]' subvalues "
94       "where <index> is a positive or negative array index",
95       name.str().c_str(), GetTypeAsCString());
96     return nullptr;
97   }
98 
99   name = name.drop_front();
100   llvm::StringRef index, sub_value;
101   std::tie(index, sub_value) = name.split(']');
102   if (index.size() == name.size()) {
103     // Couldn't find a closing bracket
104     return nullptr;
105   }
106 
107   const size_t array_count = m_values.size();
108   int32_t idx = 0;
109   if (index.getAsInteger(0, idx))
110     return nullptr;
111 
112   uint32_t new_idx = UINT32_MAX;
113   if (idx < 0) {
114     // Access from the end of the array if the index is negative
115     new_idx = array_count - idx;
116   } else {
117     // Just a standard index
118     new_idx = idx;
119   }
120 
121   if (new_idx < array_count) {
122     if (m_values[new_idx]) {
123       if (!sub_value.empty())
124         return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
125                                               will_modify, error);
126       else
127         return m_values[new_idx];
128     }
129   } else {
130     if (array_count == 0)
131       error.SetErrorStringWithFormat(
132           "index %i is not valid for an empty array", idx);
133     else if (idx > 0)
134       error.SetErrorStringWithFormat(
135           "index %i out of range, valid values are 0 through %" PRIu64,
136           idx, (uint64_t)(array_count - 1));
137     else
138       error.SetErrorStringWithFormat("negative index %i out of range, "
139                                       "valid values are -1 through "
140                                       "-%" PRIu64,
141                                       idx, (uint64_t)array_count);
142   }
143   return OptionValueSP();
144 }
145 
146 size_t OptionValueArray::GetArgs(Args &args) const {
147   args.Clear();
148   const uint32_t size = m_values.size();
149   for (uint32_t i = 0; i < size; ++i) {
150     llvm::StringRef string_value = m_values[i]->GetStringValue();
151     if (!string_value.empty())
152       args.AppendArgument(string_value);
153   }
154 
155   return args.GetArgumentCount();
156 }
157 
158 Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
159   Status error;
160   const size_t argc = args.GetArgumentCount();
161   switch (op) {
162   case eVarSetOperationInvalid:
163     error.SetErrorString("unsupported operation");
164     break;
165 
166   case eVarSetOperationInsertBefore:
167   case eVarSetOperationInsertAfter:
168     if (argc > 1) {
169       uint32_t idx;
170       const uint32_t count = GetSize();
171       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
172         error.SetErrorStringWithFormat(
173             "invalid insert array index %s, index must be 0 through %u",
174             args.GetArgumentAtIndex(0), count);
175       } else {
176         if (op == eVarSetOperationInsertAfter)
177           ++idx;
178         for (size_t i = 1; i < argc; ++i, ++idx) {
179           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
180               args.GetArgumentAtIndex(i), m_type_mask, error));
181           if (value_sp) {
182             if (error.Fail())
183               return error;
184             if (idx >= m_values.size())
185               m_values.push_back(value_sp);
186             else
187               m_values.insert(m_values.begin() + idx, value_sp);
188           } else {
189             error.SetErrorString(
190                 "array of complex types must subclass OptionValueArray");
191             return error;
192           }
193         }
194       }
195     } else {
196       error.SetErrorString("insert operation takes an array index followed by "
197                            "one or more values");
198     }
199     break;
200 
201   case eVarSetOperationRemove:
202     if (argc > 0) {
203       const uint32_t size = m_values.size();
204       std::vector<int> remove_indexes;
205       bool all_indexes_valid = true;
206       size_t i;
207       for (i = 0; i < argc; ++i) {
208         size_t idx;
209         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) {
210           all_indexes_valid = false;
211           break;
212         } else
213           remove_indexes.push_back(idx);
214       }
215 
216       if (all_indexes_valid) {
217         size_t num_remove_indexes = remove_indexes.size();
218         if (num_remove_indexes) {
219           // Sort and then erase in reverse so indexes are always valid
220           if (num_remove_indexes > 1) {
221             llvm::sort(remove_indexes);
222             for (std::vector<int>::const_reverse_iterator
223                      pos = remove_indexes.rbegin(),
224                      end = remove_indexes.rend();
225                  pos != end; ++pos) {
226               m_values.erase(m_values.begin() + *pos);
227             }
228           } else {
229             // Only one index
230             m_values.erase(m_values.begin() + remove_indexes.front());
231           }
232         }
233       } else {
234         error.SetErrorStringWithFormat(
235             "invalid array index '%s', aborting remove operation",
236             args.GetArgumentAtIndex(i));
237       }
238     } else {
239       error.SetErrorString("remove operation takes one or more array indices");
240     }
241     break;
242 
243   case eVarSetOperationClear:
244     Clear();
245     break;
246 
247   case eVarSetOperationReplace:
248     if (argc > 1) {
249       uint32_t idx;
250       const uint32_t count = GetSize();
251       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
252         error.SetErrorStringWithFormat(
253             "invalid replace array index %s, index must be 0 through %u",
254             args.GetArgumentAtIndex(0), 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 OptionValueSP
303 OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const {
304   auto copy_sp = OptionValue::DeepCopy(new_parent);
305   // copy_sp->GetAsArray cannot be used here as it doesn't work for derived
306   // types that override GetType returning a different value.
307   auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get());
308   lldbassert(array_value_ptr);
309 
310   for (auto &value : array_value_ptr->m_values)
311     value = value->DeepCopy(copy_sp);
312 
313   return copy_sp;
314 }
315