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 
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:
55bc0a9a17SJim Ingham       case eTypeFileLineColumn:
5667cc0636SGreg Clayton       case eTypeFormat:
5767cc0636SGreg Clayton       case eTypeSInt64:
5867cc0636SGreg Clayton       case eTypeString:
5967cc0636SGreg Clayton       case eTypeUInt64:
6067cc0636SGreg Clayton       case eTypeUUID:
6167cc0636SGreg Clayton         // No need to show the type for dictionaries of simple items
62b9c1b51eSKate Stone         m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
63b9c1b51eSKate Stone                                                   extra_dump_options);
6467cc0636SGreg Clayton         break;
6567cc0636SGreg Clayton       }
66b76e25a2SJonas Devlieghere 
67b76e25a2SJonas Devlieghere       if (!one_line) {
6867cc0636SGreg Clayton         if (i < (size - 1))
6967cc0636SGreg Clayton           strm.EOL();
70b76e25a2SJonas Devlieghere       } else {
71b76e25a2SJonas Devlieghere         strm << ' ';
7267cc0636SGreg Clayton       }
73b76e25a2SJonas Devlieghere     }
74b76e25a2SJonas Devlieghere     if (!one_line)
7567cc0636SGreg Clayton       strm.IndentLess();
7667cc0636SGreg Clayton   }
7767cc0636SGreg Clayton }
7867cc0636SGreg Clayton 
7997206d57SZachary Turner Status OptionValueArray::SetValueFromString(llvm::StringRef value,
80b9c1b51eSKate Stone                                             VarSetOperationType op) {
81771ef6d4SMalcolm Parsons   Args args(value.str());
8297206d57SZachary Turner   Status error = SetArgs(args, op);
838f37ca56SIlia K   if (error.Success())
84332e8b1cSGreg Clayton     NotifyValueChanged();
858f37ca56SIlia K   return error;
8667cc0636SGreg Clayton }
8767cc0636SGreg Clayton 
8867cc0636SGreg Clayton lldb::OptionValueSP
8997206d57SZachary Turner OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
9097206d57SZachary Turner                               llvm::StringRef name, bool will_modify,
9197206d57SZachary Turner                               Status &error) const {
9231d97a5cSZachary Turner   if (name.empty() || name.front() != '[') {
9331d97a5cSZachary Turner     error.SetErrorStringWithFormat(
9431d97a5cSZachary Turner       "invalid value path '%s', %s values only support '[<index>]' subvalues "
9531d97a5cSZachary Turner       "where <index> is a positive or negative array index",
9631d97a5cSZachary Turner       name.str().c_str(), GetTypeAsCString());
9731d97a5cSZachary Turner     return nullptr;
9831d97a5cSZachary Turner   }
9931d97a5cSZachary Turner 
10031d97a5cSZachary Turner   name = name.drop_front();
10131d97a5cSZachary Turner   llvm::StringRef index, sub_value;
10231d97a5cSZachary Turner   std::tie(index, sub_value) = name.split(']');
10331d97a5cSZachary Turner   if (index.size() == name.size()) {
10431d97a5cSZachary Turner     // Couldn't find a closing bracket
10531d97a5cSZachary Turner     return nullptr;
10631d97a5cSZachary Turner   }
10731d97a5cSZachary Turner 
10867cc0636SGreg Clayton   const size_t array_count = m_values.size();
10931d97a5cSZachary Turner   int32_t idx = 0;
11031d97a5cSZachary Turner   if (index.getAsInteger(0, idx))
11131d97a5cSZachary Turner     return nullptr;
11231d97a5cSZachary Turner 
11367cc0636SGreg Clayton   uint32_t new_idx = UINT32_MAX;
114b9c1b51eSKate Stone   if (idx < 0) {
11567cc0636SGreg Clayton     // Access from the end of the array if the index is negative
11667cc0636SGreg Clayton     new_idx = array_count - idx;
117b9c1b51eSKate Stone   } else {
11867cc0636SGreg Clayton     // Just a standard index
11967cc0636SGreg Clayton     new_idx = idx;
12067cc0636SGreg Clayton   }
12167cc0636SGreg Clayton 
122b9c1b51eSKate Stone   if (new_idx < array_count) {
123b9c1b51eSKate Stone     if (m_values[new_idx]) {
12431d97a5cSZachary Turner       if (!sub_value.empty())
125b9c1b51eSKate Stone         return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
126b9c1b51eSKate Stone                                               will_modify, error);
12767cc0636SGreg Clayton       else
12867cc0636SGreg Clayton         return m_values[new_idx];
12967cc0636SGreg Clayton     }
130b9c1b51eSKate Stone   } else {
13167cc0636SGreg Clayton     if (array_count == 0)
132b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
133b9c1b51eSKate Stone           "index %i is not valid for an empty array", idx);
13467cc0636SGreg Clayton     else if (idx > 0)
135b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
136b9c1b51eSKate Stone           "index %i out of range, valid values are 0 through %" PRIu64,
137b9c1b51eSKate Stone           idx, (uint64_t)(array_count - 1));
13867cc0636SGreg Clayton     else
139b9c1b51eSKate Stone       error.SetErrorStringWithFormat("negative index %i out of range, "
140b9c1b51eSKate Stone                                       "valid values are -1 through "
141b9c1b51eSKate Stone                                       "-%" PRIu64,
142b9c1b51eSKate Stone                                       idx, (uint64_t)array_count);
14367cc0636SGreg Clayton   }
14467cc0636SGreg Clayton   return OptionValueSP();
14567cc0636SGreg Clayton }
14667cc0636SGreg Clayton 
147b9c1b51eSKate Stone size_t OptionValueArray::GetArgs(Args &args) const {
14831d97a5cSZachary Turner   args.Clear();
14967cc0636SGreg Clayton   const uint32_t size = m_values.size();
150b9c1b51eSKate Stone   for (uint32_t i = 0; i < size; ++i) {
15131d97a5cSZachary Turner     llvm::StringRef string_value = m_values[i]->GetStringValue();
15231d97a5cSZachary Turner     if (!string_value.empty())
15331d97a5cSZachary Turner       args.AppendArgument(string_value);
15467cc0636SGreg Clayton   }
15567cc0636SGreg Clayton 
15667cc0636SGreg Clayton   return args.GetArgumentCount();
15767cc0636SGreg Clayton }
15867cc0636SGreg Clayton 
15997206d57SZachary Turner Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
16097206d57SZachary Turner   Status error;
16167cc0636SGreg Clayton   const size_t argc = args.GetArgumentCount();
162b9c1b51eSKate Stone   switch (op) {
16367cc0636SGreg Clayton   case eVarSetOperationInvalid:
16467cc0636SGreg Clayton     error.SetErrorString("unsupported operation");
16567cc0636SGreg Clayton     break;
16667cc0636SGreg Clayton 
16767cc0636SGreg Clayton   case eVarSetOperationInsertBefore:
16867cc0636SGreg Clayton   case eVarSetOperationInsertAfter:
169b9c1b51eSKate Stone     if (argc > 1) {
170b9c1b51eSKate Stone       uint32_t idx =
171b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
17267cc0636SGreg Clayton       const uint32_t count = GetSize();
173b9c1b51eSKate Stone       if (idx > count) {
174b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
175b9c1b51eSKate Stone             "invalid insert array index %u, index must be 0 through %u", idx,
176b9c1b51eSKate Stone             count);
177b9c1b51eSKate Stone       } else {
17867cc0636SGreg Clayton         if (op == eVarSetOperationInsertAfter)
17967cc0636SGreg Clayton           ++idx;
180b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
181b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
182b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
183b9c1b51eSKate Stone           if (value_sp) {
18467cc0636SGreg Clayton             if (error.Fail())
18567cc0636SGreg Clayton               return error;
18667cc0636SGreg Clayton             if (idx >= m_values.size())
18767cc0636SGreg Clayton               m_values.push_back(value_sp);
18867cc0636SGreg Clayton             else
18967cc0636SGreg Clayton               m_values.insert(m_values.begin() + idx, value_sp);
190b9c1b51eSKate Stone           } else {
191b9c1b51eSKate Stone             error.SetErrorString(
192b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
19367cc0636SGreg Clayton             return error;
19467cc0636SGreg Clayton           }
19567cc0636SGreg Clayton         }
19667cc0636SGreg Clayton       }
197b9c1b51eSKate Stone     } else {
198b9c1b51eSKate Stone       error.SetErrorString("insert operation takes an array index followed by "
199b9c1b51eSKate Stone                            "one or more values");
20067cc0636SGreg Clayton     }
20167cc0636SGreg Clayton     break;
20267cc0636SGreg Clayton 
20367cc0636SGreg Clayton   case eVarSetOperationRemove:
204b9c1b51eSKate Stone     if (argc > 0) {
20567cc0636SGreg Clayton       const uint32_t size = m_values.size();
20667cc0636SGreg Clayton       std::vector<int> remove_indexes;
20767cc0636SGreg Clayton       bool all_indexes_valid = true;
20867cc0636SGreg Clayton       size_t i;
209b9c1b51eSKate Stone       for (i = 0; i < argc; ++i) {
2103985c8c6SSaleem Abdulrasool         const size_t idx =
2115275aaa0SVince Harron             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
212b9c1b51eSKate Stone         if (idx >= size) {
21367cc0636SGreg Clayton           all_indexes_valid = false;
21467cc0636SGreg Clayton           break;
215b9c1b51eSKate Stone         } else
21667cc0636SGreg Clayton           remove_indexes.push_back(idx);
21767cc0636SGreg Clayton       }
21867cc0636SGreg Clayton 
219b9c1b51eSKate Stone       if (all_indexes_valid) {
22067cc0636SGreg Clayton         size_t num_remove_indexes = remove_indexes.size();
221b9c1b51eSKate Stone         if (num_remove_indexes) {
22267cc0636SGreg Clayton           // Sort and then erase in reverse so indexes are always valid
223b9c1b51eSKate Stone           if (num_remove_indexes > 1) {
2249bbba276SJonas Devlieghere             llvm::sort(remove_indexes.begin(), remove_indexes.end());
225b9c1b51eSKate Stone             for (std::vector<int>::const_reverse_iterator
226b9c1b51eSKate Stone                      pos = remove_indexes.rbegin(),
227b9c1b51eSKate Stone                      end = remove_indexes.rend();
228b9c1b51eSKate Stone                  pos != end; ++pos) {
22967cc0636SGreg Clayton               m_values.erase(m_values.begin() + *pos);
23067cc0636SGreg Clayton             }
231b9c1b51eSKate Stone           } else {
23267cc0636SGreg Clayton             // Only one index
23367cc0636SGreg Clayton             m_values.erase(m_values.begin() + remove_indexes.front());
23467cc0636SGreg Clayton           }
23567cc0636SGreg Clayton         }
236b9c1b51eSKate Stone       } else {
237b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
238b9c1b51eSKate Stone             "invalid array index '%s', aborting remove operation",
239b9c1b51eSKate Stone             args.GetArgumentAtIndex(i));
24067cc0636SGreg Clayton       }
241b9c1b51eSKate Stone     } else {
24267cc0636SGreg Clayton       error.SetErrorString("remove operation takes one or more array indices");
24367cc0636SGreg Clayton     }
24467cc0636SGreg Clayton     break;
24567cc0636SGreg Clayton 
24667cc0636SGreg Clayton   case eVarSetOperationClear:
24767cc0636SGreg Clayton     Clear();
24867cc0636SGreg Clayton     break;
24967cc0636SGreg Clayton 
25067cc0636SGreg Clayton   case eVarSetOperationReplace:
251b9c1b51eSKate Stone     if (argc > 1) {
252b9c1b51eSKate Stone       uint32_t idx =
253b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
25467cc0636SGreg Clayton       const uint32_t count = GetSize();
255b9c1b51eSKate Stone       if (idx > count) {
256b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
257b9c1b51eSKate Stone             "invalid replace array index %u, index must be 0 through %u", idx,
258b9c1b51eSKate Stone             count);
259b9c1b51eSKate Stone       } else {
260b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
261b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
262b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
263b9c1b51eSKate Stone           if (value_sp) {
26467cc0636SGreg Clayton             if (error.Fail())
26567cc0636SGreg Clayton               return error;
26667cc0636SGreg Clayton             if (idx < count)
26767cc0636SGreg Clayton               m_values[idx] = value_sp;
26867cc0636SGreg Clayton             else
26967cc0636SGreg Clayton               m_values.push_back(value_sp);
270b9c1b51eSKate Stone           } else {
271b9c1b51eSKate Stone             error.SetErrorString(
272b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
27367cc0636SGreg Clayton             return error;
27467cc0636SGreg Clayton           }
27567cc0636SGreg Clayton         }
27667cc0636SGreg Clayton       }
277b9c1b51eSKate Stone     } else {
278b9c1b51eSKate Stone       error.SetErrorString("replace operation takes an array index followed by "
279b9c1b51eSKate Stone                            "one or more values");
28067cc0636SGreg Clayton     }
28167cc0636SGreg Clayton     break;
28267cc0636SGreg Clayton 
28367cc0636SGreg Clayton   case eVarSetOperationAssign:
28467cc0636SGreg Clayton     m_values.clear();
28567cc0636SGreg Clayton     // Fall through to append case
28662e0681aSJason Molenda     LLVM_FALLTHROUGH;
28767cc0636SGreg Clayton   case eVarSetOperationAppend:
288b9c1b51eSKate Stone     for (size_t i = 0; i < argc; ++i) {
289b9c1b51eSKate Stone       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
290b9c1b51eSKate Stone           args.GetArgumentAtIndex(i), m_type_mask, error));
291b9c1b51eSKate Stone       if (value_sp) {
29267cc0636SGreg Clayton         if (error.Fail())
29367cc0636SGreg Clayton           return error;
29467cc0636SGreg Clayton         m_value_was_set = true;
29567cc0636SGreg Clayton         AppendValue(value_sp);
296b9c1b51eSKate Stone       } else {
297b9c1b51eSKate Stone         error.SetErrorString(
298b9c1b51eSKate Stone             "array of complex types must subclass OptionValueArray");
29967cc0636SGreg Clayton       }
30067cc0636SGreg Clayton     }
30167cc0636SGreg Clayton     break;
30267cc0636SGreg Clayton   }
30367cc0636SGreg Clayton   return error;
30467cc0636SGreg Clayton }
30567cc0636SGreg Clayton 
306*f0f183eeSTatyana Krasnukha OptionValueSP
307*f0f183eeSTatyana Krasnukha OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const {
308*f0f183eeSTatyana Krasnukha   auto copy_sp = OptionValue::DeepCopy(new_parent);
309*f0f183eeSTatyana Krasnukha   // copy_sp->GetAsArray cannot be used here as it doesn't work for derived
310*f0f183eeSTatyana Krasnukha   // types that override GetType returning a different value.
311*f0f183eeSTatyana Krasnukha   auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get());
312*f0f183eeSTatyana Krasnukha   lldbassert(array_value_ptr);
313*f0f183eeSTatyana Krasnukha 
314*f0f183eeSTatyana Krasnukha   for (auto &value : array_value_ptr->m_values)
315*f0f183eeSTatyana Krasnukha     value = value->DeepCopy(copy_sp);
316*f0f183eeSTatyana Krasnukha 
317*f0f183eeSTatyana Krasnukha   return copy_sp;
31867cc0636SGreg Clayton }
319