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
15using namespace lldb;
16using namespace lldb_private;
17
18void 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
78Status 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
87lldb::OptionValueSP
88OptionValueArray::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
146size_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
158Status 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
305lldb::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