1dda28197Spatrick //===-- OptionValueArray.cpp ----------------------------------------------===// 2061da546Spatrick // 3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick // 7061da546Spatrick //===----------------------------------------------------------------------===// 8061da546Spatrick 9061da546Spatrick #include "lldb/Interpreter/OptionValueArray.h" 10061da546Spatrick 11061da546Spatrick #include "lldb/Host/StringConvert.h" 12061da546Spatrick #include "lldb/Utility/Args.h" 13061da546Spatrick #include "lldb/Utility/Stream.h" 14061da546Spatrick 15061da546Spatrick using namespace lldb; 16061da546Spatrick using namespace lldb_private; 17061da546Spatrick 18061da546Spatrick void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, 19061da546Spatrick uint32_t dump_mask) { 20061da546Spatrick const Type array_element_type = ConvertTypeMaskToType(m_type_mask); 21061da546Spatrick if (dump_mask & eDumpOptionType) { 22061da546Spatrick if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) 23061da546Spatrick strm.Printf("(%s of %ss)", GetTypeAsCString(), 24061da546Spatrick GetBuiltinTypeAsCString(array_element_type)); 25061da546Spatrick else 26061da546Spatrick strm.Printf("(%s)", GetTypeAsCString()); 27061da546Spatrick } 28061da546Spatrick if (dump_mask & eDumpOptionValue) { 29061da546Spatrick const bool one_line = dump_mask & eDumpOptionCommand; 30061da546Spatrick const uint32_t size = m_values.size(); 31061da546Spatrick if (dump_mask & eDumpOptionType) 32061da546Spatrick strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); 33061da546Spatrick if (!one_line) 34061da546Spatrick strm.IndentMore(); 35061da546Spatrick for (uint32_t i = 0; i < size; ++i) { 36061da546Spatrick if (!one_line) { 37061da546Spatrick strm.Indent(); 38061da546Spatrick strm.Printf("[%u]: ", i); 39061da546Spatrick } 40061da546Spatrick const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; 41061da546Spatrick switch (array_element_type) { 42061da546Spatrick default: 43061da546Spatrick case eTypeArray: 44061da546Spatrick case eTypeDictionary: 45061da546Spatrick case eTypeProperties: 46061da546Spatrick case eTypeFileSpecList: 47061da546Spatrick case eTypePathMap: 48061da546Spatrick m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); 49061da546Spatrick break; 50061da546Spatrick 51061da546Spatrick case eTypeBoolean: 52061da546Spatrick case eTypeChar: 53061da546Spatrick case eTypeEnum: 54061da546Spatrick case eTypeFileSpec: 55*be691f3bSpatrick case eTypeFileLineColumn: 56061da546Spatrick case eTypeFormat: 57061da546Spatrick case eTypeSInt64: 58061da546Spatrick case eTypeString: 59061da546Spatrick case eTypeUInt64: 60061da546Spatrick case eTypeUUID: 61061da546Spatrick // No need to show the type for dictionaries of simple items 62061da546Spatrick m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | 63061da546Spatrick extra_dump_options); 64061da546Spatrick break; 65061da546Spatrick } 66061da546Spatrick 67061da546Spatrick if (!one_line) { 68061da546Spatrick if (i < (size - 1)) 69061da546Spatrick strm.EOL(); 70061da546Spatrick } else { 71061da546Spatrick strm << ' '; 72061da546Spatrick } 73061da546Spatrick } 74061da546Spatrick if (!one_line) 75061da546Spatrick strm.IndentLess(); 76061da546Spatrick } 77061da546Spatrick } 78061da546Spatrick 79061da546Spatrick Status OptionValueArray::SetValueFromString(llvm::StringRef value, 80061da546Spatrick VarSetOperationType op) { 81061da546Spatrick Args args(value.str()); 82061da546Spatrick Status error = SetArgs(args, op); 83061da546Spatrick if (error.Success()) 84061da546Spatrick NotifyValueChanged(); 85061da546Spatrick return error; 86061da546Spatrick } 87061da546Spatrick 88061da546Spatrick lldb::OptionValueSP 89061da546Spatrick OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx, 90061da546Spatrick llvm::StringRef name, bool will_modify, 91061da546Spatrick Status &error) const { 92061da546Spatrick if (name.empty() || name.front() != '[') { 93061da546Spatrick error.SetErrorStringWithFormat( 94061da546Spatrick "invalid value path '%s', %s values only support '[<index>]' subvalues " 95061da546Spatrick "where <index> is a positive or negative array index", 96061da546Spatrick name.str().c_str(), GetTypeAsCString()); 97061da546Spatrick return nullptr; 98061da546Spatrick } 99061da546Spatrick 100061da546Spatrick name = name.drop_front(); 101061da546Spatrick llvm::StringRef index, sub_value; 102061da546Spatrick std::tie(index, sub_value) = name.split(']'); 103061da546Spatrick if (index.size() == name.size()) { 104061da546Spatrick // Couldn't find a closing bracket 105061da546Spatrick return nullptr; 106061da546Spatrick } 107061da546Spatrick 108061da546Spatrick const size_t array_count = m_values.size(); 109061da546Spatrick int32_t idx = 0; 110061da546Spatrick if (index.getAsInteger(0, idx)) 111061da546Spatrick return nullptr; 112061da546Spatrick 113061da546Spatrick uint32_t new_idx = UINT32_MAX; 114061da546Spatrick if (idx < 0) { 115061da546Spatrick // Access from the end of the array if the index is negative 116061da546Spatrick new_idx = array_count - idx; 117061da546Spatrick } else { 118061da546Spatrick // Just a standard index 119061da546Spatrick new_idx = idx; 120061da546Spatrick } 121061da546Spatrick 122061da546Spatrick if (new_idx < array_count) { 123061da546Spatrick if (m_values[new_idx]) { 124061da546Spatrick if (!sub_value.empty()) 125061da546Spatrick return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, 126061da546Spatrick will_modify, error); 127061da546Spatrick else 128061da546Spatrick return m_values[new_idx]; 129061da546Spatrick } 130061da546Spatrick } else { 131061da546Spatrick if (array_count == 0) 132061da546Spatrick error.SetErrorStringWithFormat( 133061da546Spatrick "index %i is not valid for an empty array", idx); 134061da546Spatrick else if (idx > 0) 135061da546Spatrick error.SetErrorStringWithFormat( 136061da546Spatrick "index %i out of range, valid values are 0 through %" PRIu64, 137061da546Spatrick idx, (uint64_t)(array_count - 1)); 138061da546Spatrick else 139061da546Spatrick error.SetErrorStringWithFormat("negative index %i out of range, " 140061da546Spatrick "valid values are -1 through " 141061da546Spatrick "-%" PRIu64, 142061da546Spatrick idx, (uint64_t)array_count); 143061da546Spatrick } 144061da546Spatrick return OptionValueSP(); 145061da546Spatrick } 146061da546Spatrick 147061da546Spatrick size_t OptionValueArray::GetArgs(Args &args) const { 148061da546Spatrick args.Clear(); 149061da546Spatrick const uint32_t size = m_values.size(); 150061da546Spatrick for (uint32_t i = 0; i < size; ++i) { 151061da546Spatrick llvm::StringRef string_value = m_values[i]->GetStringValue(); 152061da546Spatrick if (!string_value.empty()) 153061da546Spatrick args.AppendArgument(string_value); 154061da546Spatrick } 155061da546Spatrick 156061da546Spatrick return args.GetArgumentCount(); 157061da546Spatrick } 158061da546Spatrick 159061da546Spatrick Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { 160061da546Spatrick Status error; 161061da546Spatrick const size_t argc = args.GetArgumentCount(); 162061da546Spatrick switch (op) { 163061da546Spatrick case eVarSetOperationInvalid: 164061da546Spatrick error.SetErrorString("unsupported operation"); 165061da546Spatrick break; 166061da546Spatrick 167061da546Spatrick case eVarSetOperationInsertBefore: 168061da546Spatrick case eVarSetOperationInsertAfter: 169061da546Spatrick if (argc > 1) { 170061da546Spatrick uint32_t idx = 171061da546Spatrick StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 172061da546Spatrick const uint32_t count = GetSize(); 173061da546Spatrick if (idx > count) { 174061da546Spatrick error.SetErrorStringWithFormat( 175061da546Spatrick "invalid insert array index %u, index must be 0 through %u", idx, 176061da546Spatrick count); 177061da546Spatrick } else { 178061da546Spatrick if (op == eVarSetOperationInsertAfter) 179061da546Spatrick ++idx; 180061da546Spatrick for (size_t i = 1; i < argc; ++i, ++idx) { 181061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 182061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 183061da546Spatrick if (value_sp) { 184061da546Spatrick if (error.Fail()) 185061da546Spatrick return error; 186061da546Spatrick if (idx >= m_values.size()) 187061da546Spatrick m_values.push_back(value_sp); 188061da546Spatrick else 189061da546Spatrick m_values.insert(m_values.begin() + idx, value_sp); 190061da546Spatrick } else { 191061da546Spatrick error.SetErrorString( 192061da546Spatrick "array of complex types must subclass OptionValueArray"); 193061da546Spatrick return error; 194061da546Spatrick } 195061da546Spatrick } 196061da546Spatrick } 197061da546Spatrick } else { 198061da546Spatrick error.SetErrorString("insert operation takes an array index followed by " 199061da546Spatrick "one or more values"); 200061da546Spatrick } 201061da546Spatrick break; 202061da546Spatrick 203061da546Spatrick case eVarSetOperationRemove: 204061da546Spatrick if (argc > 0) { 205061da546Spatrick const uint32_t size = m_values.size(); 206061da546Spatrick std::vector<int> remove_indexes; 207061da546Spatrick bool all_indexes_valid = true; 208061da546Spatrick size_t i; 209061da546Spatrick for (i = 0; i < argc; ++i) { 210061da546Spatrick const size_t idx = 211061da546Spatrick StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX); 212061da546Spatrick if (idx >= size) { 213061da546Spatrick all_indexes_valid = false; 214061da546Spatrick break; 215061da546Spatrick } else 216061da546Spatrick remove_indexes.push_back(idx); 217061da546Spatrick } 218061da546Spatrick 219061da546Spatrick if (all_indexes_valid) { 220061da546Spatrick size_t num_remove_indexes = remove_indexes.size(); 221061da546Spatrick if (num_remove_indexes) { 222061da546Spatrick // Sort and then erase in reverse so indexes are always valid 223061da546Spatrick if (num_remove_indexes > 1) { 224061da546Spatrick llvm::sort(remove_indexes.begin(), remove_indexes.end()); 225061da546Spatrick for (std::vector<int>::const_reverse_iterator 226061da546Spatrick pos = remove_indexes.rbegin(), 227061da546Spatrick end = remove_indexes.rend(); 228061da546Spatrick pos != end; ++pos) { 229061da546Spatrick m_values.erase(m_values.begin() + *pos); 230061da546Spatrick } 231061da546Spatrick } else { 232061da546Spatrick // Only one index 233061da546Spatrick m_values.erase(m_values.begin() + remove_indexes.front()); 234061da546Spatrick } 235061da546Spatrick } 236061da546Spatrick } else { 237061da546Spatrick error.SetErrorStringWithFormat( 238061da546Spatrick "invalid array index '%s', aborting remove operation", 239061da546Spatrick args.GetArgumentAtIndex(i)); 240061da546Spatrick } 241061da546Spatrick } else { 242061da546Spatrick error.SetErrorString("remove operation takes one or more array indices"); 243061da546Spatrick } 244061da546Spatrick break; 245061da546Spatrick 246061da546Spatrick case eVarSetOperationClear: 247061da546Spatrick Clear(); 248061da546Spatrick break; 249061da546Spatrick 250061da546Spatrick case eVarSetOperationReplace: 251061da546Spatrick if (argc > 1) { 252061da546Spatrick uint32_t idx = 253061da546Spatrick StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 254061da546Spatrick const uint32_t count = GetSize(); 255061da546Spatrick if (idx > count) { 256061da546Spatrick error.SetErrorStringWithFormat( 257061da546Spatrick "invalid replace array index %u, index must be 0 through %u", idx, 258061da546Spatrick count); 259061da546Spatrick } else { 260061da546Spatrick for (size_t i = 1; i < argc; ++i, ++idx) { 261061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 262061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 263061da546Spatrick if (value_sp) { 264061da546Spatrick if (error.Fail()) 265061da546Spatrick return error; 266061da546Spatrick if (idx < count) 267061da546Spatrick m_values[idx] = value_sp; 268061da546Spatrick else 269061da546Spatrick m_values.push_back(value_sp); 270061da546Spatrick } else { 271061da546Spatrick error.SetErrorString( 272061da546Spatrick "array of complex types must subclass OptionValueArray"); 273061da546Spatrick return error; 274061da546Spatrick } 275061da546Spatrick } 276061da546Spatrick } 277061da546Spatrick } else { 278061da546Spatrick error.SetErrorString("replace operation takes an array index followed by " 279061da546Spatrick "one or more values"); 280061da546Spatrick } 281061da546Spatrick break; 282061da546Spatrick 283061da546Spatrick case eVarSetOperationAssign: 284061da546Spatrick m_values.clear(); 285061da546Spatrick // Fall through to append case 286061da546Spatrick LLVM_FALLTHROUGH; 287061da546Spatrick case eVarSetOperationAppend: 288061da546Spatrick for (size_t i = 0; i < argc; ++i) { 289061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 290061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 291061da546Spatrick if (value_sp) { 292061da546Spatrick if (error.Fail()) 293061da546Spatrick return error; 294061da546Spatrick m_value_was_set = true; 295061da546Spatrick AppendValue(value_sp); 296061da546Spatrick } else { 297061da546Spatrick error.SetErrorString( 298061da546Spatrick "array of complex types must subclass OptionValueArray"); 299061da546Spatrick } 300061da546Spatrick } 301061da546Spatrick break; 302061da546Spatrick } 303061da546Spatrick return error; 304061da546Spatrick } 305061da546Spatrick 306*be691f3bSpatrick OptionValueSP 307*be691f3bSpatrick OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { 308*be691f3bSpatrick auto copy_sp = OptionValue::DeepCopy(new_parent); 309*be691f3bSpatrick // copy_sp->GetAsArray cannot be used here as it doesn't work for derived 310*be691f3bSpatrick // types that override GetType returning a different value. 311*be691f3bSpatrick auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); 312*be691f3bSpatrick lldbassert(array_value_ptr); 313*be691f3bSpatrick 314*be691f3bSpatrick for (auto &value : array_value_ptr->m_values) 315*be691f3bSpatrick value = value->DeepCopy(copy_sp); 316*be691f3bSpatrick 317*be691f3bSpatrick return copy_sp; 318061da546Spatrick } 319