180814287SRaphael Isemann //===-- OptionValueArray.cpp ----------------------------------------------===//
267cc0636SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler 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 
11145d95c9SPavel Labath #include "lldb/Utility/Args.h"
12bf9a7730SZachary Turner #include "lldb/Utility/Stream.h"
1367cc0636SGreg Clayton 
1467cc0636SGreg Clayton using namespace lldb;
1567cc0636SGreg Clayton using namespace lldb_private;
1667cc0636SGreg Clayton 
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)17b9c1b51eSKate Stone void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
18b9c1b51eSKate Stone                                  uint32_t dump_mask) {
1967cc0636SGreg Clayton   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
20b9c1b51eSKate Stone   if (dump_mask & eDumpOptionType) {
2167cc0636SGreg Clayton     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
22b9c1b51eSKate Stone       strm.Printf("(%s of %ss)", GetTypeAsCString(),
23b9c1b51eSKate Stone                   GetBuiltinTypeAsCString(array_element_type));
2467cc0636SGreg Clayton     else
2567cc0636SGreg Clayton       strm.Printf("(%s)", GetTypeAsCString());
2667cc0636SGreg Clayton   }
27b9c1b51eSKate Stone   if (dump_mask & eDumpOptionValue) {
28b76e25a2SJonas Devlieghere     const bool one_line = dump_mask & eDumpOptionCommand;
2967cc0636SGreg Clayton     const uint32_t size = m_values.size();
30b76e25a2SJonas Devlieghere     if (dump_mask & eDumpOptionType)
31b76e25a2SJonas Devlieghere       strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
32b76e25a2SJonas Devlieghere     if (!one_line)
33b76e25a2SJonas Devlieghere       strm.IndentMore();
34b9c1b51eSKate Stone     for (uint32_t i = 0; i < size; ++i) {
35b76e25a2SJonas Devlieghere       if (!one_line) {
3667cc0636SGreg Clayton         strm.Indent();
3767cc0636SGreg Clayton         strm.Printf("[%u]: ", i);
38b76e25a2SJonas Devlieghere       }
3967cc0636SGreg Clayton       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
40b9c1b51eSKate Stone       switch (array_element_type) {
4167cc0636SGreg Clayton       default:
4267cc0636SGreg Clayton       case eTypeArray:
4367cc0636SGreg Clayton       case eTypeDictionary:
4467cc0636SGreg Clayton       case eTypeProperties:
4567cc0636SGreg Clayton       case eTypeFileSpecList:
4667cc0636SGreg Clayton       case eTypePathMap:
4767cc0636SGreg Clayton         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
4867cc0636SGreg Clayton         break;
4967cc0636SGreg Clayton 
5067cc0636SGreg Clayton       case eTypeBoolean:
513e7442b6SZachary Turner       case eTypeChar:
5267cc0636SGreg Clayton       case eTypeEnum:
5367cc0636SGreg Clayton       case eTypeFileSpec:
54bc0a9a17SJim Ingham       case eTypeFileLineColumn:
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 
SetValueFromString(llvm::StringRef value,VarSetOperationType op)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
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,bool will_modify,Status & error) const8897206d57SZachary 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 
GetArgs(Args & args) const146b9c1b51eSKate 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 
SetArgs(const Args & args,VarSetOperationType op)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) {
1693a6ba367SMichał Górny       uint32_t idx;
17067cc0636SGreg Clayton       const uint32_t count = GetSize();
1713a6ba367SMichał Górny       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
172b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
1733a6ba367SMichał Górny             "invalid insert array index %s, index must be 0 through %u",
1743a6ba367SMichał Górny             args.GetArgumentAtIndex(0), count);
175b9c1b51eSKate Stone       } else {
17667cc0636SGreg Clayton         if (op == eVarSetOperationInsertAfter)
17767cc0636SGreg Clayton           ++idx;
178b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
179b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
180b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
181b9c1b51eSKate Stone           if (value_sp) {
18267cc0636SGreg Clayton             if (error.Fail())
18367cc0636SGreg Clayton               return error;
18467cc0636SGreg Clayton             if (idx >= m_values.size())
18567cc0636SGreg Clayton               m_values.push_back(value_sp);
18667cc0636SGreg Clayton             else
18767cc0636SGreg Clayton               m_values.insert(m_values.begin() + idx, value_sp);
188b9c1b51eSKate Stone           } else {
189b9c1b51eSKate Stone             error.SetErrorString(
190b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
19167cc0636SGreg Clayton             return error;
19267cc0636SGreg Clayton           }
19367cc0636SGreg Clayton         }
19467cc0636SGreg Clayton       }
195b9c1b51eSKate Stone     } else {
196b9c1b51eSKate Stone       error.SetErrorString("insert operation takes an array index followed by "
197b9c1b51eSKate Stone                            "one or more values");
19867cc0636SGreg Clayton     }
19967cc0636SGreg Clayton     break;
20067cc0636SGreg Clayton 
20167cc0636SGreg Clayton   case eVarSetOperationRemove:
202b9c1b51eSKate Stone     if (argc > 0) {
20367cc0636SGreg Clayton       const uint32_t size = m_values.size();
20467cc0636SGreg Clayton       std::vector<int> remove_indexes;
20567cc0636SGreg Clayton       bool all_indexes_valid = true;
20667cc0636SGreg Clayton       size_t i;
207b9c1b51eSKate Stone       for (i = 0; i < argc; ++i) {
2083a6ba367SMichał Górny         size_t idx;
2093a6ba367SMichał Górny         if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) {
21067cc0636SGreg Clayton           all_indexes_valid = false;
21167cc0636SGreg Clayton           break;
212b9c1b51eSKate Stone         } else
21367cc0636SGreg Clayton           remove_indexes.push_back(idx);
21467cc0636SGreg Clayton       }
21567cc0636SGreg Clayton 
216b9c1b51eSKate Stone       if (all_indexes_valid) {
21767cc0636SGreg Clayton         size_t num_remove_indexes = remove_indexes.size();
218b9c1b51eSKate Stone         if (num_remove_indexes) {
21967cc0636SGreg Clayton           // Sort and then erase in reverse so indexes are always valid
220b9c1b51eSKate Stone           if (num_remove_indexes > 1) {
221*cd9a5cfdSDmitri Gribenko             llvm::sort(remove_indexes);
222b9c1b51eSKate Stone             for (std::vector<int>::const_reverse_iterator
223b9c1b51eSKate Stone                      pos = remove_indexes.rbegin(),
224b9c1b51eSKate Stone                      end = remove_indexes.rend();
225b9c1b51eSKate Stone                  pos != end; ++pos) {
22667cc0636SGreg Clayton               m_values.erase(m_values.begin() + *pos);
22767cc0636SGreg Clayton             }
228b9c1b51eSKate Stone           } else {
22967cc0636SGreg Clayton             // Only one index
23067cc0636SGreg Clayton             m_values.erase(m_values.begin() + remove_indexes.front());
23167cc0636SGreg Clayton           }
23267cc0636SGreg Clayton         }
233b9c1b51eSKate Stone       } else {
234b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
235b9c1b51eSKate Stone             "invalid array index '%s', aborting remove operation",
236b9c1b51eSKate Stone             args.GetArgumentAtIndex(i));
23767cc0636SGreg Clayton       }
238b9c1b51eSKate Stone     } else {
23967cc0636SGreg Clayton       error.SetErrorString("remove operation takes one or more array indices");
24067cc0636SGreg Clayton     }
24167cc0636SGreg Clayton     break;
24267cc0636SGreg Clayton 
24367cc0636SGreg Clayton   case eVarSetOperationClear:
24467cc0636SGreg Clayton     Clear();
24567cc0636SGreg Clayton     break;
24667cc0636SGreg Clayton 
24767cc0636SGreg Clayton   case eVarSetOperationReplace:
248b9c1b51eSKate Stone     if (argc > 1) {
2493a6ba367SMichał Górny       uint32_t idx;
25067cc0636SGreg Clayton       const uint32_t count = GetSize();
2513a6ba367SMichał Górny       if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) {
252b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
2533a6ba367SMichał Górny             "invalid replace array index %s, index must be 0 through %u",
2543a6ba367SMichał Górny             args.GetArgumentAtIndex(0), count);
255b9c1b51eSKate Stone       } else {
256b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
257b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
258b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
259b9c1b51eSKate Stone           if (value_sp) {
26067cc0636SGreg Clayton             if (error.Fail())
26167cc0636SGreg Clayton               return error;
26267cc0636SGreg Clayton             if (idx < count)
26367cc0636SGreg Clayton               m_values[idx] = value_sp;
26467cc0636SGreg Clayton             else
26567cc0636SGreg Clayton               m_values.push_back(value_sp);
266b9c1b51eSKate Stone           } else {
267b9c1b51eSKate Stone             error.SetErrorString(
268b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
26967cc0636SGreg Clayton             return error;
27067cc0636SGreg Clayton           }
27167cc0636SGreg Clayton         }
27267cc0636SGreg Clayton       }
273b9c1b51eSKate Stone     } else {
274b9c1b51eSKate Stone       error.SetErrorString("replace operation takes an array index followed by "
275b9c1b51eSKate Stone                            "one or more values");
27667cc0636SGreg Clayton     }
27767cc0636SGreg Clayton     break;
27867cc0636SGreg Clayton 
27967cc0636SGreg Clayton   case eVarSetOperationAssign:
28067cc0636SGreg Clayton     m_values.clear();
28167cc0636SGreg Clayton     // Fall through to append case
28262e0681aSJason Molenda     LLVM_FALLTHROUGH;
28367cc0636SGreg Clayton   case eVarSetOperationAppend:
284b9c1b51eSKate Stone     for (size_t i = 0; i < argc; ++i) {
285b9c1b51eSKate Stone       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
286b9c1b51eSKate Stone           args.GetArgumentAtIndex(i), m_type_mask, error));
287b9c1b51eSKate Stone       if (value_sp) {
28867cc0636SGreg Clayton         if (error.Fail())
28967cc0636SGreg Clayton           return error;
29067cc0636SGreg Clayton         m_value_was_set = true;
29167cc0636SGreg Clayton         AppendValue(value_sp);
292b9c1b51eSKate Stone       } else {
293b9c1b51eSKate Stone         error.SetErrorString(
294b9c1b51eSKate Stone             "array of complex types must subclass OptionValueArray");
29567cc0636SGreg Clayton       }
29667cc0636SGreg Clayton     }
29767cc0636SGreg Clayton     break;
29867cc0636SGreg Clayton   }
29967cc0636SGreg Clayton   return error;
30067cc0636SGreg Clayton }
30167cc0636SGreg Clayton 
302f0f183eeSTatyana Krasnukha OptionValueSP
DeepCopy(const OptionValueSP & new_parent) const303f0f183eeSTatyana Krasnukha OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const {
304f0f183eeSTatyana Krasnukha   auto copy_sp = OptionValue::DeepCopy(new_parent);
305f0f183eeSTatyana Krasnukha   // copy_sp->GetAsArray cannot be used here as it doesn't work for derived
306f0f183eeSTatyana Krasnukha   // types that override GetType returning a different value.
307f0f183eeSTatyana Krasnukha   auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get());
308f0f183eeSTatyana Krasnukha   lldbassert(array_value_ptr);
309f0f183eeSTatyana Krasnukha 
310f0f183eeSTatyana Krasnukha   for (auto &value : array_value_ptr->m_values)
311f0f183eeSTatyana Krasnukha     value = value->DeepCopy(copy_sp);
312f0f183eeSTatyana Krasnukha 
313f0f183eeSTatyana Krasnukha   return copy_sp;
31467cc0636SGreg Clayton }
315