167cc0636SGreg Clayton //===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===//
267cc0636SGreg Clayton //
3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667cc0636SGreg Clayton //
767cc0636SGreg Clayton //===----------------------------------------------------------------------===//
867cc0636SGreg Clayton 
967cc0636SGreg Clayton #include "lldb/Interpreter/OptionValueArray.h"
1067cc0636SGreg Clayton 
115275aaa0SVince Harron #include "lldb/Host/StringConvert.h"
12145d95c9SPavel Labath #include "lldb/Utility/Args.h"
13bf9a7730SZachary Turner #include "lldb/Utility/Stream.h"
1467cc0636SGreg Clayton 
1567cc0636SGreg Clayton using namespace lldb;
1667cc0636SGreg Clayton using namespace lldb_private;
1767cc0636SGreg Clayton 
18b9c1b51eSKate Stone void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
19b9c1b51eSKate Stone                                  uint32_t dump_mask) {
2067cc0636SGreg Clayton   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
21b9c1b51eSKate Stone   if (dump_mask & eDumpOptionType) {
2267cc0636SGreg Clayton     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
23b9c1b51eSKate Stone       strm.Printf("(%s of %ss)", GetTypeAsCString(),
24b9c1b51eSKate Stone                   GetBuiltinTypeAsCString(array_element_type));
2567cc0636SGreg Clayton     else
2667cc0636SGreg Clayton       strm.Printf("(%s)", GetTypeAsCString());
2767cc0636SGreg Clayton   }
28b9c1b51eSKate Stone   if (dump_mask & eDumpOptionValue) {
29b76e25a2SJonas Devlieghere     const bool one_line = dump_mask & eDumpOptionCommand;
3067cc0636SGreg Clayton     const uint32_t size = m_values.size();
31b76e25a2SJonas Devlieghere     if (dump_mask & eDumpOptionType)
32b76e25a2SJonas Devlieghere       strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
33b76e25a2SJonas Devlieghere     if (!one_line)
34b76e25a2SJonas Devlieghere       strm.IndentMore();
35b9c1b51eSKate Stone     for (uint32_t i = 0; i < size; ++i) {
36b76e25a2SJonas Devlieghere       if (!one_line) {
3767cc0636SGreg Clayton         strm.Indent();
3867cc0636SGreg Clayton         strm.Printf("[%u]: ", i);
39b76e25a2SJonas Devlieghere       }
4067cc0636SGreg Clayton       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
41b9c1b51eSKate Stone       switch (array_element_type) {
4267cc0636SGreg Clayton       default:
4367cc0636SGreg Clayton       case eTypeArray:
4467cc0636SGreg Clayton       case eTypeDictionary:
4567cc0636SGreg Clayton       case eTypeProperties:
4667cc0636SGreg Clayton       case eTypeFileSpecList:
4767cc0636SGreg Clayton       case eTypePathMap:
4867cc0636SGreg Clayton         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
4967cc0636SGreg Clayton         break;
5067cc0636SGreg Clayton 
5167cc0636SGreg Clayton       case eTypeBoolean:
523e7442b6SZachary Turner       case eTypeChar:
5367cc0636SGreg Clayton       case eTypeEnum:
5467cc0636SGreg Clayton       case eTypeFileSpec:
5567cc0636SGreg Clayton       case eTypeFormat:
5667cc0636SGreg Clayton       case eTypeSInt64:
5767cc0636SGreg Clayton       case eTypeString:
5867cc0636SGreg Clayton       case eTypeUInt64:
5967cc0636SGreg Clayton       case eTypeUUID:
6067cc0636SGreg Clayton         // No need to show the type for dictionaries of simple items
61b9c1b51eSKate Stone         m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
62b9c1b51eSKate Stone                                                   extra_dump_options);
6367cc0636SGreg Clayton         break;
6467cc0636SGreg Clayton       }
65b76e25a2SJonas Devlieghere 
66b76e25a2SJonas Devlieghere       if (!one_line) {
6767cc0636SGreg Clayton         if (i < (size - 1))
6867cc0636SGreg Clayton           strm.EOL();
69b76e25a2SJonas Devlieghere       } else {
70b76e25a2SJonas Devlieghere         strm << ' ';
7167cc0636SGreg Clayton       }
72b76e25a2SJonas Devlieghere     }
73b76e25a2SJonas Devlieghere     if (!one_line)
7467cc0636SGreg Clayton       strm.IndentLess();
7567cc0636SGreg Clayton   }
7667cc0636SGreg Clayton }
7767cc0636SGreg Clayton 
7897206d57SZachary Turner Status OptionValueArray::SetValueFromString(llvm::StringRef value,
79b9c1b51eSKate Stone                                             VarSetOperationType op) {
80771ef6d4SMalcolm Parsons   Args args(value.str());
8197206d57SZachary Turner   Status error = SetArgs(args, op);
828f37ca56SIlia K   if (error.Success())
83332e8b1cSGreg Clayton     NotifyValueChanged();
848f37ca56SIlia K   return error;
8567cc0636SGreg Clayton }
8667cc0636SGreg Clayton 
8767cc0636SGreg Clayton lldb::OptionValueSP
8897206d57SZachary Turner OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
8997206d57SZachary Turner                               llvm::StringRef name, bool will_modify,
9097206d57SZachary Turner                               Status &error) const {
9131d97a5cSZachary Turner   if (name.empty() || name.front() != '[') {
9231d97a5cSZachary Turner     error.SetErrorStringWithFormat(
9331d97a5cSZachary Turner       "invalid value path '%s', %s values only support '[<index>]' subvalues "
9431d97a5cSZachary Turner       "where <index> is a positive or negative array index",
9531d97a5cSZachary Turner       name.str().c_str(), GetTypeAsCString());
9631d97a5cSZachary Turner     return nullptr;
9731d97a5cSZachary Turner   }
9831d97a5cSZachary Turner 
9931d97a5cSZachary Turner   name = name.drop_front();
10031d97a5cSZachary Turner   llvm::StringRef index, sub_value;
10131d97a5cSZachary Turner   std::tie(index, sub_value) = name.split(']');
10231d97a5cSZachary Turner   if (index.size() == name.size()) {
10331d97a5cSZachary Turner     // Couldn't find a closing bracket
10431d97a5cSZachary Turner     return nullptr;
10531d97a5cSZachary Turner   }
10631d97a5cSZachary Turner 
10767cc0636SGreg Clayton   const size_t array_count = m_values.size();
10831d97a5cSZachary Turner   int32_t idx = 0;
10931d97a5cSZachary Turner   if (index.getAsInteger(0, idx))
11031d97a5cSZachary Turner     return nullptr;
11131d97a5cSZachary Turner 
11267cc0636SGreg Clayton   uint32_t new_idx = UINT32_MAX;
113b9c1b51eSKate Stone   if (idx < 0) {
11467cc0636SGreg Clayton     // Access from the end of the array if the index is negative
11567cc0636SGreg Clayton     new_idx = array_count - idx;
116b9c1b51eSKate Stone   } else {
11767cc0636SGreg Clayton     // Just a standard index
11867cc0636SGreg Clayton     new_idx = idx;
11967cc0636SGreg Clayton   }
12067cc0636SGreg Clayton 
121b9c1b51eSKate Stone   if (new_idx < array_count) {
122b9c1b51eSKate Stone     if (m_values[new_idx]) {
12331d97a5cSZachary Turner       if (!sub_value.empty())
124b9c1b51eSKate Stone         return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
125b9c1b51eSKate Stone                                               will_modify, error);
12667cc0636SGreg Clayton       else
12767cc0636SGreg Clayton         return m_values[new_idx];
12867cc0636SGreg Clayton     }
129b9c1b51eSKate Stone   } else {
13067cc0636SGreg Clayton     if (array_count == 0)
131b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
132b9c1b51eSKate Stone           "index %i is not valid for an empty array", idx);
13367cc0636SGreg Clayton     else if (idx > 0)
134b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
135b9c1b51eSKate Stone           "index %i out of range, valid values are 0 through %" PRIu64,
136b9c1b51eSKate Stone           idx, (uint64_t)(array_count - 1));
13767cc0636SGreg Clayton     else
138b9c1b51eSKate Stone       error.SetErrorStringWithFormat("negative index %i out of range, "
139b9c1b51eSKate Stone                                       "valid values are -1 through "
140b9c1b51eSKate Stone                                       "-%" PRIu64,
141b9c1b51eSKate Stone                                       idx, (uint64_t)array_count);
14267cc0636SGreg Clayton   }
14367cc0636SGreg Clayton   return OptionValueSP();
14467cc0636SGreg Clayton }
14567cc0636SGreg Clayton 
146b9c1b51eSKate Stone size_t OptionValueArray::GetArgs(Args &args) const {
14731d97a5cSZachary Turner   args.Clear();
14867cc0636SGreg Clayton   const uint32_t size = m_values.size();
149b9c1b51eSKate Stone   for (uint32_t i = 0; i < size; ++i) {
15031d97a5cSZachary Turner     llvm::StringRef string_value = m_values[i]->GetStringValue();
15131d97a5cSZachary Turner     if (!string_value.empty())
15231d97a5cSZachary Turner       args.AppendArgument(string_value);
15367cc0636SGreg Clayton   }
15467cc0636SGreg Clayton 
15567cc0636SGreg Clayton   return args.GetArgumentCount();
15667cc0636SGreg Clayton }
15767cc0636SGreg Clayton 
15897206d57SZachary Turner Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
15997206d57SZachary Turner   Status error;
16067cc0636SGreg Clayton   const size_t argc = args.GetArgumentCount();
161b9c1b51eSKate Stone   switch (op) {
16267cc0636SGreg Clayton   case eVarSetOperationInvalid:
16367cc0636SGreg Clayton     error.SetErrorString("unsupported operation");
16467cc0636SGreg Clayton     break;
16567cc0636SGreg Clayton 
16667cc0636SGreg Clayton   case eVarSetOperationInsertBefore:
16767cc0636SGreg Clayton   case eVarSetOperationInsertAfter:
168b9c1b51eSKate Stone     if (argc > 1) {
169b9c1b51eSKate Stone       uint32_t idx =
170b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
17167cc0636SGreg Clayton       const uint32_t count = GetSize();
172b9c1b51eSKate Stone       if (idx > count) {
173b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
174b9c1b51eSKate Stone             "invalid insert array index %u, index must be 0 through %u", idx,
175b9c1b51eSKate Stone             count);
176b9c1b51eSKate Stone       } else {
17767cc0636SGreg Clayton         if (op == eVarSetOperationInsertAfter)
17867cc0636SGreg Clayton           ++idx;
179b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
180b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
181b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
182b9c1b51eSKate Stone           if (value_sp) {
18367cc0636SGreg Clayton             if (error.Fail())
18467cc0636SGreg Clayton               return error;
18567cc0636SGreg Clayton             if (idx >= m_values.size())
18667cc0636SGreg Clayton               m_values.push_back(value_sp);
18767cc0636SGreg Clayton             else
18867cc0636SGreg Clayton               m_values.insert(m_values.begin() + idx, value_sp);
189b9c1b51eSKate Stone           } else {
190b9c1b51eSKate Stone             error.SetErrorString(
191b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
19267cc0636SGreg Clayton             return error;
19367cc0636SGreg Clayton           }
19467cc0636SGreg Clayton         }
19567cc0636SGreg Clayton       }
196b9c1b51eSKate Stone     } else {
197b9c1b51eSKate Stone       error.SetErrorString("insert operation takes an array index followed by "
198b9c1b51eSKate Stone                            "one or more values");
19967cc0636SGreg Clayton     }
20067cc0636SGreg Clayton     break;
20167cc0636SGreg Clayton 
20267cc0636SGreg Clayton   case eVarSetOperationRemove:
203b9c1b51eSKate Stone     if (argc > 0) {
20467cc0636SGreg Clayton       const uint32_t size = m_values.size();
20567cc0636SGreg Clayton       std::vector<int> remove_indexes;
20667cc0636SGreg Clayton       bool all_indexes_valid = true;
20767cc0636SGreg Clayton       size_t i;
208b9c1b51eSKate Stone       for (i = 0; i < argc; ++i) {
2093985c8c6SSaleem Abdulrasool         const size_t idx =
2105275aaa0SVince Harron             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
211b9c1b51eSKate Stone         if (idx >= size) {
21267cc0636SGreg Clayton           all_indexes_valid = false;
21367cc0636SGreg Clayton           break;
214b9c1b51eSKate Stone         } else
21567cc0636SGreg Clayton           remove_indexes.push_back(idx);
21667cc0636SGreg Clayton       }
21767cc0636SGreg Clayton 
218b9c1b51eSKate Stone       if (all_indexes_valid) {
21967cc0636SGreg Clayton         size_t num_remove_indexes = remove_indexes.size();
220b9c1b51eSKate Stone         if (num_remove_indexes) {
22167cc0636SGreg Clayton           // Sort and then erase in reverse so indexes are always valid
222b9c1b51eSKate Stone           if (num_remove_indexes > 1) {
2239bbba276SJonas Devlieghere             llvm::sort(remove_indexes.begin(), remove_indexes.end());
224b9c1b51eSKate Stone             for (std::vector<int>::const_reverse_iterator
225b9c1b51eSKate Stone                      pos = remove_indexes.rbegin(),
226b9c1b51eSKate Stone                      end = remove_indexes.rend();
227b9c1b51eSKate Stone                  pos != end; ++pos) {
22867cc0636SGreg Clayton               m_values.erase(m_values.begin() + *pos);
22967cc0636SGreg Clayton             }
230b9c1b51eSKate Stone           } else {
23167cc0636SGreg Clayton             // Only one index
23267cc0636SGreg Clayton             m_values.erase(m_values.begin() + remove_indexes.front());
23367cc0636SGreg Clayton           }
23467cc0636SGreg Clayton         }
235b9c1b51eSKate Stone       } else {
236b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
237b9c1b51eSKate Stone             "invalid array index '%s', aborting remove operation",
238b9c1b51eSKate Stone             args.GetArgumentAtIndex(i));
23967cc0636SGreg Clayton       }
240b9c1b51eSKate Stone     } else {
24167cc0636SGreg Clayton       error.SetErrorString("remove operation takes one or more array indices");
24267cc0636SGreg Clayton     }
24367cc0636SGreg Clayton     break;
24467cc0636SGreg Clayton 
24567cc0636SGreg Clayton   case eVarSetOperationClear:
24667cc0636SGreg Clayton     Clear();
24767cc0636SGreg Clayton     break;
24867cc0636SGreg Clayton 
24967cc0636SGreg Clayton   case eVarSetOperationReplace:
250b9c1b51eSKate Stone     if (argc > 1) {
251b9c1b51eSKate Stone       uint32_t idx =
252b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
25367cc0636SGreg Clayton       const uint32_t count = GetSize();
254b9c1b51eSKate Stone       if (idx > count) {
255b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
256b9c1b51eSKate Stone             "invalid replace array index %u, index must be 0 through %u", idx,
257b9c1b51eSKate Stone             count);
258b9c1b51eSKate Stone       } else {
259b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
260b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
261b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
262b9c1b51eSKate Stone           if (value_sp) {
26367cc0636SGreg Clayton             if (error.Fail())
26467cc0636SGreg Clayton               return error;
26567cc0636SGreg Clayton             if (idx < count)
26667cc0636SGreg Clayton               m_values[idx] = value_sp;
26767cc0636SGreg Clayton             else
26867cc0636SGreg Clayton               m_values.push_back(value_sp);
269b9c1b51eSKate Stone           } else {
270b9c1b51eSKate Stone             error.SetErrorString(
271b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
27267cc0636SGreg Clayton             return error;
27367cc0636SGreg Clayton           }
27467cc0636SGreg Clayton         }
27567cc0636SGreg Clayton       }
276b9c1b51eSKate Stone     } else {
277b9c1b51eSKate Stone       error.SetErrorString("replace operation takes an array index followed by "
278b9c1b51eSKate Stone                            "one or more values");
27967cc0636SGreg Clayton     }
28067cc0636SGreg Clayton     break;
28167cc0636SGreg Clayton 
28267cc0636SGreg Clayton   case eVarSetOperationAssign:
28367cc0636SGreg Clayton     m_values.clear();
28467cc0636SGreg Clayton     // Fall through to append case
28562e0681aSJason Molenda     LLVM_FALLTHROUGH;
28667cc0636SGreg Clayton   case eVarSetOperationAppend:
287b9c1b51eSKate Stone     for (size_t i = 0; i < argc; ++i) {
288b9c1b51eSKate Stone       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
289b9c1b51eSKate Stone           args.GetArgumentAtIndex(i), m_type_mask, error));
290b9c1b51eSKate Stone       if (value_sp) {
29167cc0636SGreg Clayton         if (error.Fail())
29267cc0636SGreg Clayton           return error;
29367cc0636SGreg Clayton         m_value_was_set = true;
29467cc0636SGreg Clayton         AppendValue(value_sp);
295b9c1b51eSKate Stone       } else {
296b9c1b51eSKate Stone         error.SetErrorString(
297b9c1b51eSKate Stone             "array of complex types must subclass OptionValueArray");
29867cc0636SGreg Clayton       }
29967cc0636SGreg Clayton     }
30067cc0636SGreg Clayton     break;
30167cc0636SGreg Clayton   }
30267cc0636SGreg Clayton   return error;
30367cc0636SGreg Clayton }
30467cc0636SGreg Clayton 
305b9c1b51eSKate Stone lldb::OptionValueSP OptionValueArray::DeepCopy() const {
306b9c1b51eSKate Stone   OptionValueArray *copied_array =
307b9c1b51eSKate Stone       new OptionValueArray(m_type_mask, m_raw_value_dump);
30867cc0636SGreg Clayton   lldb::OptionValueSP copied_value_sp(copied_array);
3098f37ca56SIlia K   *static_cast<OptionValue *>(copied_array) = *this;
3108f37ca56SIlia K   copied_array->m_callback = m_callback;
31167cc0636SGreg Clayton   const uint32_t size = m_values.size();
312b9c1b51eSKate Stone   for (uint32_t i = 0; i < size; ++i) {
31367cc0636SGreg Clayton     copied_array->AppendValue(m_values[i]->DeepCopy());
31467cc0636SGreg Clayton   }
31567cc0636SGreg Clayton   return copied_value_sp;
31667cc0636SGreg Clayton }
317