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
14using namespace lldb;
15using namespace lldb_private;
16
17void 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
78llvm::json::Value OptionValueArray::ToJSON(const ExecutionContext *exe_ctx) {
79  llvm::json::Array json_array;
80  const uint32_t size = m_values.size();
81  for (uint32_t i = 0; i < size; ++i)
82    json_array.emplace_back(m_values[i]->ToJSON(exe_ctx));
83  return json_array;
84}
85
86Status OptionValueArray::SetValueFromString(llvm::StringRef value,
87                                            VarSetOperationType op) {
88  Args args(value.str());
89  Status error = SetArgs(args, op);
90  if (error.Success())
91    NotifyValueChanged();
92  return error;
93}
94
95lldb::OptionValueSP
96OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
97                              llvm::StringRef name, Status &error) const {
98  if (name.empty() || name.front() != '[') {
99    error.SetErrorStringWithFormat(
100      "invalid value path '%s', %s values only support '[<index>]' subvalues "
101      "where <index> is a positive or negative array index",
102      name.str().c_str(), GetTypeAsCString());
103    return nullptr;
104  }
105
106  name = name.drop_front();
107  llvm::StringRef index, sub_value;
108  std::tie(index, sub_value) = name.split(']');
109  if (index.size() == name.size()) {
110    // Couldn't find a closing bracket
111    return nullptr;
112  }
113
114  const size_t array_count = m_values.size();
115  int32_t idx = 0;
116  if (index.getAsInteger(0, idx))
117    return nullptr;
118
119  uint32_t new_idx = UINT32_MAX;
120  if (idx < 0) {
121    // Access from the end of the array if the index is negative
122    new_idx = array_count - idx;
123  } else {
124    // Just a standard index
125    new_idx = idx;
126  }
127
128  if (new_idx < array_count) {
129    if (m_values[new_idx]) {
130      if (!sub_value.empty())
131        return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, error);
132      else
133        return m_values[new_idx];
134    }
135  } else {
136    if (array_count == 0)
137      error.SetErrorStringWithFormat(
138          "index %i is not valid for an empty array", idx);
139    else if (idx > 0)
140      error.SetErrorStringWithFormat(
141          "index %i out of range, valid values are 0 through %" PRIu64,
142          idx, (uint64_t)(array_count - 1));
143    else
144      error.SetErrorStringWithFormat("negative index %i out of range, "
145                                      "valid values are -1 through "
146                                      "-%" PRIu64,
147                                      idx, (uint64_t)array_count);
148  }
149  return OptionValueSP();
150}
151
152size_t OptionValueArray::GetArgs(Args &args) const {
153  args.Clear();
154  const uint32_t size = m_values.size();
155  for (uint32_t i = 0; i < size; ++i) {
156    auto string_value = m_values[i]->GetValueAs<llvm::StringRef>();
157    if (string_value)
158      args.AppendArgument(*string_value);
159  }
160
161  return args.GetArgumentCount();
162}
163
164Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
165  Status error;
166  const size_t argc = args.GetArgumentCount();
167  switch (op) {
168  case eVarSetOperationInvalid:
169    error.SetErrorString("unsupported operation");
170    break;
171
172  case eVarSetOperationInsertBefore:
173  case eVarSetOperationInsertAfter:
174    if (argc > 1) {
175      uint32_t idx;
176      const uint32_t count = GetSize();
177      if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
178        error.SetErrorStringWithFormat(
179            "invalid insert array index %s, index must be 0 through %u",
180            args.GetArgumentAtIndex(0), count);
181      } else {
182        if (op == eVarSetOperationInsertAfter)
183          ++idx;
184        for (size_t i = 1; i < argc; ++i, ++idx) {
185          lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
186              args.GetArgumentAtIndex(i), m_type_mask, error));
187          if (value_sp) {
188            if (error.Fail())
189              return error;
190            if (idx >= m_values.size())
191              m_values.push_back(value_sp);
192            else
193              m_values.insert(m_values.begin() + idx, value_sp);
194          } else {
195            error.SetErrorString(
196                "array of complex types must subclass OptionValueArray");
197            return error;
198          }
199        }
200      }
201    } else {
202      error.SetErrorString("insert operation takes an array index followed by "
203                           "one or more values");
204    }
205    break;
206
207  case eVarSetOperationRemove:
208    if (argc > 0) {
209      const uint32_t size = m_values.size();
210      std::vector<int> remove_indexes;
211      bool all_indexes_valid = true;
212      size_t i;
213      for (i = 0; i < argc; ++i) {
214        size_t idx;
215        if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) {
216          all_indexes_valid = false;
217          break;
218        } else
219          remove_indexes.push_back(idx);
220      }
221
222      if (all_indexes_valid) {
223        size_t num_remove_indexes = remove_indexes.size();
224        if (num_remove_indexes) {
225          // Sort and then erase in reverse so indexes are always valid
226          if (num_remove_indexes > 1) {
227            llvm::sort(remove_indexes);
228            for (std::vector<int>::const_reverse_iterator
229                     pos = remove_indexes.rbegin(),
230                     end = remove_indexes.rend();
231                 pos != end; ++pos) {
232              m_values.erase(m_values.begin() + *pos);
233            }
234          } else {
235            // Only one index
236            m_values.erase(m_values.begin() + remove_indexes.front());
237          }
238        }
239      } else {
240        error.SetErrorStringWithFormat(
241            "invalid array index '%s', aborting remove operation",
242            args.GetArgumentAtIndex(i));
243      }
244    } else {
245      error.SetErrorString("remove operation takes one or more array indices");
246    }
247    break;
248
249  case eVarSetOperationClear:
250    Clear();
251    break;
252
253  case eVarSetOperationReplace:
254    if (argc > 1) {
255      uint32_t idx;
256      const uint32_t count = GetSize();
257      if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
258        error.SetErrorStringWithFormat(
259            "invalid replace array index %s, index must be 0 through %u",
260            args.GetArgumentAtIndex(0), count);
261      } else {
262        for (size_t i = 1; i < argc; ++i, ++idx) {
263          lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
264              args.GetArgumentAtIndex(i), m_type_mask, error));
265          if (value_sp) {
266            if (error.Fail())
267              return error;
268            if (idx < count)
269              m_values[idx] = value_sp;
270            else
271              m_values.push_back(value_sp);
272          } else {
273            error.SetErrorString(
274                "array of complex types must subclass OptionValueArray");
275            return error;
276          }
277        }
278      }
279    } else {
280      error.SetErrorString("replace operation takes an array index followed by "
281                           "one or more values");
282    }
283    break;
284
285  case eVarSetOperationAssign:
286    m_values.clear();
287    // Fall through to append case
288    [[fallthrough]];
289  case eVarSetOperationAppend:
290    for (size_t i = 0; i < argc; ++i) {
291      lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
292          args.GetArgumentAtIndex(i), m_type_mask, error));
293      if (value_sp) {
294        if (error.Fail())
295          return error;
296        m_value_was_set = true;
297        AppendValue(value_sp);
298      } else {
299        error.SetErrorString(
300            "array of complex types must subclass OptionValueArray");
301      }
302    }
303    break;
304  }
305  return error;
306}
307
308OptionValueSP
309OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const {
310  auto copy_sp = OptionValue::DeepCopy(new_parent);
311  // copy_sp->GetAsArray cannot be used here as it doesn't work for derived
312  // types that override GetType returning a different value.
313  auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get());
314  lldbassert(array_value_ptr);
315
316  for (auto &value : array_value_ptr->m_values)
317    value = value->DeepCopy(copy_sp);
318
319  return copy_sp;
320}
321