1 //===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===//
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/Host/StringConvert.h"
12 #include "lldb/Utility/Args.h"
13 #include "lldb/Utility/Stream.h"
14 
15 using namespace lldb;
16 using namespace lldb_private;
17 
18 void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
19                                  uint32_t dump_mask) {
20   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
21   if (dump_mask & eDumpOptionType) {
22     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
23       strm.Printf("(%s of %ss)", GetTypeAsCString(),
24                   GetBuiltinTypeAsCString(array_element_type));
25     else
26       strm.Printf("(%s)", GetTypeAsCString());
27   }
28   if (dump_mask & eDumpOptionValue) {
29     const bool one_line = dump_mask & eDumpOptionCommand;
30     const uint32_t size = m_values.size();
31     if (dump_mask & eDumpOptionType)
32       strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
33     if (!one_line)
34       strm.IndentMore();
35     for (uint32_t i = 0; i < size; ++i) {
36       if (!one_line) {
37         strm.Indent();
38         strm.Printf("[%u]: ", i);
39       }
40       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
41       switch (array_element_type) {
42       default:
43       case eTypeArray:
44       case eTypeDictionary:
45       case eTypeProperties:
46       case eTypeFileSpecList:
47       case eTypePathMap:
48         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
49         break;
50 
51       case eTypeBoolean:
52       case eTypeChar:
53       case eTypeEnum:
54       case eTypeFileSpec:
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           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
171       const uint32_t count = GetSize();
172       if (idx > count) {
173         error.SetErrorStringWithFormat(
174             "invalid insert array index %u, index must be 0 through %u", idx,
175             count);
176       } else {
177         if (op == eVarSetOperationInsertAfter)
178           ++idx;
179         for (size_t i = 1; i < argc; ++i, ++idx) {
180           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
181               args.GetArgumentAtIndex(i), m_type_mask, error));
182           if (value_sp) {
183             if (error.Fail())
184               return error;
185             if (idx >= m_values.size())
186               m_values.push_back(value_sp);
187             else
188               m_values.insert(m_values.begin() + idx, value_sp);
189           } else {
190             error.SetErrorString(
191                 "array of complex types must subclass OptionValueArray");
192             return error;
193           }
194         }
195       }
196     } else {
197       error.SetErrorString("insert operation takes an array index followed by "
198                            "one or more values");
199     }
200     break;
201 
202   case eVarSetOperationRemove:
203     if (argc > 0) {
204       const uint32_t size = m_values.size();
205       std::vector<int> remove_indexes;
206       bool all_indexes_valid = true;
207       size_t i;
208       for (i = 0; i < argc; ++i) {
209         const size_t idx =
210             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
211         if (idx >= size) {
212           all_indexes_valid = false;
213           break;
214         } else
215           remove_indexes.push_back(idx);
216       }
217 
218       if (all_indexes_valid) {
219         size_t num_remove_indexes = remove_indexes.size();
220         if (num_remove_indexes) {
221           // Sort and then erase in reverse so indexes are always valid
222           if (num_remove_indexes > 1) {
223             llvm::sort(remove_indexes.begin(), remove_indexes.end());
224             for (std::vector<int>::const_reverse_iterator
225                      pos = remove_indexes.rbegin(),
226                      end = remove_indexes.rend();
227                  pos != end; ++pos) {
228               m_values.erase(m_values.begin() + *pos);
229             }
230           } else {
231             // Only one index
232             m_values.erase(m_values.begin() + remove_indexes.front());
233           }
234         }
235       } else {
236         error.SetErrorStringWithFormat(
237             "invalid array index '%s', aborting remove operation",
238             args.GetArgumentAtIndex(i));
239       }
240     } else {
241       error.SetErrorString("remove operation takes one or more array indices");
242     }
243     break;
244 
245   case eVarSetOperationClear:
246     Clear();
247     break;
248 
249   case eVarSetOperationReplace:
250     if (argc > 1) {
251       uint32_t idx =
252           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
253       const uint32_t count = GetSize();
254       if (idx > count) {
255         error.SetErrorStringWithFormat(
256             "invalid replace array index %u, index must be 0 through %u", idx,
257             count);
258       } else {
259         for (size_t i = 1; i < argc; ++i, ++idx) {
260           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
261               args.GetArgumentAtIndex(i), m_type_mask, error));
262           if (value_sp) {
263             if (error.Fail())
264               return error;
265             if (idx < count)
266               m_values[idx] = value_sp;
267             else
268               m_values.push_back(value_sp);
269           } else {
270             error.SetErrorString(
271                 "array of complex types must subclass OptionValueArray");
272             return error;
273           }
274         }
275       }
276     } else {
277       error.SetErrorString("replace operation takes an array index followed by "
278                            "one or more values");
279     }
280     break;
281 
282   case eVarSetOperationAssign:
283     m_values.clear();
284     // Fall through to append case
285     LLVM_FALLTHROUGH;
286   case eVarSetOperationAppend:
287     for (size_t i = 0; i < argc; ++i) {
288       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
289           args.GetArgumentAtIndex(i), m_type_mask, error));
290       if (value_sp) {
291         if (error.Fail())
292           return error;
293         m_value_was_set = true;
294         AppendValue(value_sp);
295       } else {
296         error.SetErrorString(
297             "array of complex types must subclass OptionValueArray");
298       }
299     }
300     break;
301   }
302   return error;
303 }
304 
305 lldb::OptionValueSP OptionValueArray::DeepCopy() const {
306   OptionValueArray *copied_array =
307       new OptionValueArray(m_type_mask, m_raw_value_dump);
308   lldb::OptionValueSP copied_value_sp(copied_array);
309   *static_cast<OptionValue *>(copied_array) = *this;
310   copied_array->m_callback = m_callback;
311   const uint32_t size = m_values.size();
312   for (uint32_t i = 0; i < size; ++i) {
313     copied_array->AppendValue(m_values[i]->DeepCopy());
314   }
315   return copied_value_sp;
316 }
317