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 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 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) { 169*3a6ba367SMichał Górny uint32_t idx; 17067cc0636SGreg Clayton const uint32_t count = GetSize(); 171*3a6ba367SMichał Górny if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 172b9c1b51eSKate Stone error.SetErrorStringWithFormat( 173*3a6ba367SMichał Górny "invalid insert array index %s, index must be 0 through %u", 174*3a6ba367SMichał 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) { 208*3a6ba367SMichał Górny size_t idx; 209*3a6ba367SMichał 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) { 2219bbba276SJonas Devlieghere llvm::sort(remove_indexes.begin(), remove_indexes.end()); 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) { 249*3a6ba367SMichał Górny uint32_t idx; 25067cc0636SGreg Clayton const uint32_t count = GetSize(); 251*3a6ba367SMichał Górny if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 252b9c1b51eSKate Stone error.SetErrorStringWithFormat( 253*3a6ba367SMichał Górny "invalid replace array index %s, index must be 0 through %u", 254*3a6ba367SMichał 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 303f0f183eeSTatyana 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