167cc0636SGreg Clayton //===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===//
267cc0636SGreg Clayton //
367cc0636SGreg Clayton //                     The LLVM Compiler Infrastructure
467cc0636SGreg Clayton //
567cc0636SGreg Clayton // This file is distributed under the University of Illinois Open Source
667cc0636SGreg Clayton // License. See LICENSE.TXT for details.
767cc0636SGreg Clayton //
867cc0636SGreg Clayton //===----------------------------------------------------------------------===//
967cc0636SGreg Clayton 
1067cc0636SGreg Clayton #include "lldb/Interpreter/OptionValueArray.h"
1167cc0636SGreg Clayton 
1267cc0636SGreg Clayton // C Includes
1367cc0636SGreg Clayton // C++ Includes
1467cc0636SGreg Clayton // Other libraries and framework includes
1567cc0636SGreg Clayton // Project includes
165275aaa0SVince Harron #include "lldb/Host/StringConvert.h"
17145d95c9SPavel Labath #include "lldb/Utility/Args.h"
18bf9a7730SZachary Turner #include "lldb/Utility/Stream.h"
1967cc0636SGreg Clayton 
2067cc0636SGreg Clayton using namespace lldb;
2167cc0636SGreg Clayton using namespace lldb_private;
2267cc0636SGreg Clayton 
23b9c1b51eSKate Stone void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
24b9c1b51eSKate Stone                                  uint32_t dump_mask) {
2567cc0636SGreg Clayton   const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
26b9c1b51eSKate Stone   if (dump_mask & eDumpOptionType) {
2767cc0636SGreg Clayton     if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
28b9c1b51eSKate Stone       strm.Printf("(%s of %ss)", GetTypeAsCString(),
29b9c1b51eSKate Stone                   GetBuiltinTypeAsCString(array_element_type));
3067cc0636SGreg Clayton     else
3167cc0636SGreg Clayton       strm.Printf("(%s)", GetTypeAsCString());
3267cc0636SGreg Clayton   }
33b9c1b51eSKate Stone   if (dump_mask & eDumpOptionValue) {
34*b76e25a2SJonas Devlieghere     const bool one_line = dump_mask & eDumpOptionCommand;
3567cc0636SGreg Clayton     const uint32_t size = m_values.size();
36*b76e25a2SJonas Devlieghere     if (dump_mask & eDumpOptionType)
37*b76e25a2SJonas Devlieghere       strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
38*b76e25a2SJonas Devlieghere     if (!one_line)
39*b76e25a2SJonas Devlieghere       strm.IndentMore();
40b9c1b51eSKate Stone     for (uint32_t i = 0; i < size; ++i) {
41*b76e25a2SJonas Devlieghere       if (!one_line) {
4267cc0636SGreg Clayton         strm.Indent();
4367cc0636SGreg Clayton         strm.Printf("[%u]: ", i);
44*b76e25a2SJonas Devlieghere       }
4567cc0636SGreg Clayton       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
46b9c1b51eSKate Stone       switch (array_element_type) {
4767cc0636SGreg Clayton       default:
4867cc0636SGreg Clayton       case eTypeArray:
4967cc0636SGreg Clayton       case eTypeDictionary:
5067cc0636SGreg Clayton       case eTypeProperties:
5167cc0636SGreg Clayton       case eTypeFileSpecList:
5267cc0636SGreg Clayton       case eTypePathMap:
5367cc0636SGreg Clayton         m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
5467cc0636SGreg Clayton         break;
5567cc0636SGreg Clayton 
5667cc0636SGreg Clayton       case eTypeBoolean:
573e7442b6SZachary Turner       case eTypeChar:
5867cc0636SGreg Clayton       case eTypeEnum:
5967cc0636SGreg Clayton       case eTypeFileSpec:
6067cc0636SGreg Clayton       case eTypeFormat:
6167cc0636SGreg Clayton       case eTypeSInt64:
6267cc0636SGreg Clayton       case eTypeString:
6367cc0636SGreg Clayton       case eTypeUInt64:
6467cc0636SGreg Clayton       case eTypeUUID:
6567cc0636SGreg Clayton         // No need to show the type for dictionaries of simple items
66b9c1b51eSKate Stone         m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
67b9c1b51eSKate Stone                                                   extra_dump_options);
6867cc0636SGreg Clayton         break;
6967cc0636SGreg Clayton       }
70*b76e25a2SJonas Devlieghere 
71*b76e25a2SJonas Devlieghere       if (!one_line) {
7267cc0636SGreg Clayton         if (i < (size - 1))
7367cc0636SGreg Clayton           strm.EOL();
74*b76e25a2SJonas Devlieghere       } else {
75*b76e25a2SJonas Devlieghere         strm << ' ';
7667cc0636SGreg Clayton       }
77*b76e25a2SJonas Devlieghere     }
78*b76e25a2SJonas Devlieghere     if (!one_line)
7967cc0636SGreg Clayton       strm.IndentLess();
8067cc0636SGreg Clayton   }
8167cc0636SGreg Clayton }
8267cc0636SGreg Clayton 
8397206d57SZachary Turner Status OptionValueArray::SetValueFromString(llvm::StringRef value,
84b9c1b51eSKate Stone                                             VarSetOperationType op) {
85771ef6d4SMalcolm Parsons   Args args(value.str());
8697206d57SZachary Turner   Status error = SetArgs(args, op);
878f37ca56SIlia K   if (error.Success())
88332e8b1cSGreg Clayton     NotifyValueChanged();
898f37ca56SIlia K   return error;
9067cc0636SGreg Clayton }
9167cc0636SGreg Clayton 
9267cc0636SGreg Clayton lldb::OptionValueSP
9397206d57SZachary Turner OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
9497206d57SZachary Turner                               llvm::StringRef name, bool will_modify,
9597206d57SZachary Turner                               Status &error) const {
9631d97a5cSZachary Turner   if (name.empty() || name.front() != '[') {
9731d97a5cSZachary Turner     error.SetErrorStringWithFormat(
9831d97a5cSZachary Turner       "invalid value path '%s', %s values only support '[<index>]' subvalues "
9931d97a5cSZachary Turner       "where <index> is a positive or negative array index",
10031d97a5cSZachary Turner       name.str().c_str(), GetTypeAsCString());
10131d97a5cSZachary Turner     return nullptr;
10231d97a5cSZachary Turner   }
10331d97a5cSZachary Turner 
10431d97a5cSZachary Turner   name = name.drop_front();
10531d97a5cSZachary Turner   llvm::StringRef index, sub_value;
10631d97a5cSZachary Turner   std::tie(index, sub_value) = name.split(']');
10731d97a5cSZachary Turner   if (index.size() == name.size()) {
10831d97a5cSZachary Turner     // Couldn't find a closing bracket
10931d97a5cSZachary Turner     return nullptr;
11031d97a5cSZachary Turner   }
11131d97a5cSZachary Turner 
11267cc0636SGreg Clayton   const size_t array_count = m_values.size();
11331d97a5cSZachary Turner   int32_t idx = 0;
11431d97a5cSZachary Turner   if (index.getAsInteger(0, idx))
11531d97a5cSZachary Turner     return nullptr;
11631d97a5cSZachary Turner 
11767cc0636SGreg Clayton   uint32_t new_idx = UINT32_MAX;
118b9c1b51eSKate Stone   if (idx < 0) {
11967cc0636SGreg Clayton     // Access from the end of the array if the index is negative
12067cc0636SGreg Clayton     new_idx = array_count - idx;
121b9c1b51eSKate Stone   } else {
12267cc0636SGreg Clayton     // Just a standard index
12367cc0636SGreg Clayton     new_idx = idx;
12467cc0636SGreg Clayton   }
12567cc0636SGreg Clayton 
126b9c1b51eSKate Stone   if (new_idx < array_count) {
127b9c1b51eSKate Stone     if (m_values[new_idx]) {
12831d97a5cSZachary Turner       if (!sub_value.empty())
129b9c1b51eSKate Stone         return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
130b9c1b51eSKate Stone                                               will_modify, error);
13167cc0636SGreg Clayton       else
13267cc0636SGreg Clayton         return m_values[new_idx];
13367cc0636SGreg Clayton     }
134b9c1b51eSKate Stone   } else {
13567cc0636SGreg Clayton     if (array_count == 0)
136b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
137b9c1b51eSKate Stone           "index %i is not valid for an empty array", idx);
13867cc0636SGreg Clayton     else if (idx > 0)
139b9c1b51eSKate Stone       error.SetErrorStringWithFormat(
140b9c1b51eSKate Stone           "index %i out of range, valid values are 0 through %" PRIu64,
141b9c1b51eSKate Stone           idx, (uint64_t)(array_count - 1));
14267cc0636SGreg Clayton     else
143b9c1b51eSKate Stone       error.SetErrorStringWithFormat("negative index %i out of range, "
144b9c1b51eSKate Stone                                       "valid values are -1 through "
145b9c1b51eSKate Stone                                       "-%" PRIu64,
146b9c1b51eSKate Stone                                       idx, (uint64_t)array_count);
14767cc0636SGreg Clayton   }
14867cc0636SGreg Clayton   return OptionValueSP();
14967cc0636SGreg Clayton }
15067cc0636SGreg Clayton 
151b9c1b51eSKate Stone size_t OptionValueArray::GetArgs(Args &args) const {
15231d97a5cSZachary Turner   args.Clear();
15367cc0636SGreg Clayton   const uint32_t size = m_values.size();
154b9c1b51eSKate Stone   for (uint32_t i = 0; i < size; ++i) {
15531d97a5cSZachary Turner     llvm::StringRef string_value = m_values[i]->GetStringValue();
15631d97a5cSZachary Turner     if (!string_value.empty())
15731d97a5cSZachary Turner       args.AppendArgument(string_value);
15867cc0636SGreg Clayton   }
15967cc0636SGreg Clayton 
16067cc0636SGreg Clayton   return args.GetArgumentCount();
16167cc0636SGreg Clayton }
16267cc0636SGreg Clayton 
16397206d57SZachary Turner Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
16497206d57SZachary Turner   Status error;
16567cc0636SGreg Clayton   const size_t argc = args.GetArgumentCount();
166b9c1b51eSKate Stone   switch (op) {
16767cc0636SGreg Clayton   case eVarSetOperationInvalid:
16867cc0636SGreg Clayton     error.SetErrorString("unsupported operation");
16967cc0636SGreg Clayton     break;
17067cc0636SGreg Clayton 
17167cc0636SGreg Clayton   case eVarSetOperationInsertBefore:
17267cc0636SGreg Clayton   case eVarSetOperationInsertAfter:
173b9c1b51eSKate Stone     if (argc > 1) {
174b9c1b51eSKate Stone       uint32_t idx =
175b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
17667cc0636SGreg Clayton       const uint32_t count = GetSize();
177b9c1b51eSKate Stone       if (idx > count) {
178b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
179b9c1b51eSKate Stone             "invalid insert array index %u, index must be 0 through %u", idx,
180b9c1b51eSKate Stone             count);
181b9c1b51eSKate Stone       } else {
18267cc0636SGreg Clayton         if (op == eVarSetOperationInsertAfter)
18367cc0636SGreg Clayton           ++idx;
184b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
185b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
186b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
187b9c1b51eSKate Stone           if (value_sp) {
18867cc0636SGreg Clayton             if (error.Fail())
18967cc0636SGreg Clayton               return error;
19067cc0636SGreg Clayton             if (idx >= m_values.size())
19167cc0636SGreg Clayton               m_values.push_back(value_sp);
19267cc0636SGreg Clayton             else
19367cc0636SGreg Clayton               m_values.insert(m_values.begin() + idx, value_sp);
194b9c1b51eSKate Stone           } else {
195b9c1b51eSKate Stone             error.SetErrorString(
196b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
19767cc0636SGreg Clayton             return error;
19867cc0636SGreg Clayton           }
19967cc0636SGreg Clayton         }
20067cc0636SGreg Clayton       }
201b9c1b51eSKate Stone     } else {
202b9c1b51eSKate Stone       error.SetErrorString("insert operation takes an array index followed by "
203b9c1b51eSKate Stone                            "one or more values");
20467cc0636SGreg Clayton     }
20567cc0636SGreg Clayton     break;
20667cc0636SGreg Clayton 
20767cc0636SGreg Clayton   case eVarSetOperationRemove:
208b9c1b51eSKate Stone     if (argc > 0) {
20967cc0636SGreg Clayton       const uint32_t size = m_values.size();
21067cc0636SGreg Clayton       std::vector<int> remove_indexes;
21167cc0636SGreg Clayton       bool all_indexes_valid = true;
21267cc0636SGreg Clayton       size_t i;
213b9c1b51eSKate Stone       for (i = 0; i < argc; ++i) {
2143985c8c6SSaleem Abdulrasool         const size_t idx =
2155275aaa0SVince Harron             StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
216b9c1b51eSKate Stone         if (idx >= size) {
21767cc0636SGreg Clayton           all_indexes_valid = false;
21867cc0636SGreg Clayton           break;
219b9c1b51eSKate Stone         } else
22067cc0636SGreg Clayton           remove_indexes.push_back(idx);
22167cc0636SGreg Clayton       }
22267cc0636SGreg Clayton 
223b9c1b51eSKate Stone       if (all_indexes_valid) {
22467cc0636SGreg Clayton         size_t num_remove_indexes = remove_indexes.size();
225b9c1b51eSKate Stone         if (num_remove_indexes) {
22667cc0636SGreg Clayton           // Sort and then erase in reverse so indexes are always valid
227b9c1b51eSKate Stone           if (num_remove_indexes > 1) {
22867cc0636SGreg Clayton             std::sort(remove_indexes.begin(), remove_indexes.end());
229b9c1b51eSKate Stone             for (std::vector<int>::const_reverse_iterator
230b9c1b51eSKate Stone                      pos = remove_indexes.rbegin(),
231b9c1b51eSKate Stone                      end = remove_indexes.rend();
232b9c1b51eSKate Stone                  pos != end; ++pos) {
23367cc0636SGreg Clayton               m_values.erase(m_values.begin() + *pos);
23467cc0636SGreg Clayton             }
235b9c1b51eSKate Stone           } else {
23667cc0636SGreg Clayton             // Only one index
23767cc0636SGreg Clayton             m_values.erase(m_values.begin() + remove_indexes.front());
23867cc0636SGreg Clayton           }
23967cc0636SGreg Clayton         }
240b9c1b51eSKate Stone       } else {
241b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
242b9c1b51eSKate Stone             "invalid array index '%s', aborting remove operation",
243b9c1b51eSKate Stone             args.GetArgumentAtIndex(i));
24467cc0636SGreg Clayton       }
245b9c1b51eSKate Stone     } else {
24667cc0636SGreg Clayton       error.SetErrorString("remove operation takes one or more array indices");
24767cc0636SGreg Clayton     }
24867cc0636SGreg Clayton     break;
24967cc0636SGreg Clayton 
25067cc0636SGreg Clayton   case eVarSetOperationClear:
25167cc0636SGreg Clayton     Clear();
25267cc0636SGreg Clayton     break;
25367cc0636SGreg Clayton 
25467cc0636SGreg Clayton   case eVarSetOperationReplace:
255b9c1b51eSKate Stone     if (argc > 1) {
256b9c1b51eSKate Stone       uint32_t idx =
257b9c1b51eSKate Stone           StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
25867cc0636SGreg Clayton       const uint32_t count = GetSize();
259b9c1b51eSKate Stone       if (idx > count) {
260b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
261b9c1b51eSKate Stone             "invalid replace array index %u, index must be 0 through %u", idx,
262b9c1b51eSKate Stone             count);
263b9c1b51eSKate Stone       } else {
264b9c1b51eSKate Stone         for (size_t i = 1; i < argc; ++i, ++idx) {
265b9c1b51eSKate Stone           lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
266b9c1b51eSKate Stone               args.GetArgumentAtIndex(i), m_type_mask, error));
267b9c1b51eSKate Stone           if (value_sp) {
26867cc0636SGreg Clayton             if (error.Fail())
26967cc0636SGreg Clayton               return error;
27067cc0636SGreg Clayton             if (idx < count)
27167cc0636SGreg Clayton               m_values[idx] = value_sp;
27267cc0636SGreg Clayton             else
27367cc0636SGreg Clayton               m_values.push_back(value_sp);
274b9c1b51eSKate Stone           } else {
275b9c1b51eSKate Stone             error.SetErrorString(
276b9c1b51eSKate Stone                 "array of complex types must subclass OptionValueArray");
27767cc0636SGreg Clayton             return error;
27867cc0636SGreg Clayton           }
27967cc0636SGreg Clayton         }
28067cc0636SGreg Clayton       }
281b9c1b51eSKate Stone     } else {
282b9c1b51eSKate Stone       error.SetErrorString("replace operation takes an array index followed by "
283b9c1b51eSKate Stone                            "one or more values");
28467cc0636SGreg Clayton     }
28567cc0636SGreg Clayton     break;
28667cc0636SGreg Clayton 
28767cc0636SGreg Clayton   case eVarSetOperationAssign:
28867cc0636SGreg Clayton     m_values.clear();
28967cc0636SGreg Clayton     // Fall through to append case
29062e0681aSJason Molenda     LLVM_FALLTHROUGH;
29167cc0636SGreg Clayton   case eVarSetOperationAppend:
292b9c1b51eSKate Stone     for (size_t i = 0; i < argc; ++i) {
293b9c1b51eSKate Stone       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
294b9c1b51eSKate Stone           args.GetArgumentAtIndex(i), m_type_mask, error));
295b9c1b51eSKate Stone       if (value_sp) {
29667cc0636SGreg Clayton         if (error.Fail())
29767cc0636SGreg Clayton           return error;
29867cc0636SGreg Clayton         m_value_was_set = true;
29967cc0636SGreg Clayton         AppendValue(value_sp);
300b9c1b51eSKate Stone       } else {
301b9c1b51eSKate Stone         error.SetErrorString(
302b9c1b51eSKate Stone             "array of complex types must subclass OptionValueArray");
30367cc0636SGreg Clayton       }
30467cc0636SGreg Clayton     }
30567cc0636SGreg Clayton     break;
30667cc0636SGreg Clayton   }
30767cc0636SGreg Clayton   return error;
30867cc0636SGreg Clayton }
30967cc0636SGreg Clayton 
310b9c1b51eSKate Stone lldb::OptionValueSP OptionValueArray::DeepCopy() const {
311b9c1b51eSKate Stone   OptionValueArray *copied_array =
312b9c1b51eSKate Stone       new OptionValueArray(m_type_mask, m_raw_value_dump);
31367cc0636SGreg Clayton   lldb::OptionValueSP copied_value_sp(copied_array);
3148f37ca56SIlia K   *static_cast<OptionValue *>(copied_array) = *this;
3158f37ca56SIlia K   copied_array->m_callback = m_callback;
31667cc0636SGreg Clayton   const uint32_t size = m_values.size();
317b9c1b51eSKate Stone   for (uint32_t i = 0; i < size; ++i) {
31867cc0636SGreg Clayton     copied_array->AppendValue(m_values[i]->DeepCopy());
31967cc0636SGreg Clayton   }
32067cc0636SGreg Clayton   return copied_value_sp;
32167cc0636SGreg Clayton }
322