1*061da546Spatrick //===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===// 2*061da546Spatrick // 3*061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*061da546Spatrick // 7*061da546Spatrick //===----------------------------------------------------------------------===// 8*061da546Spatrick 9*061da546Spatrick #include "lldb/Interpreter/OptionValueArray.h" 10*061da546Spatrick 11*061da546Spatrick #include "lldb/Host/StringConvert.h" 12*061da546Spatrick #include "lldb/Utility/Args.h" 13*061da546Spatrick #include "lldb/Utility/Stream.h" 14*061da546Spatrick 15*061da546Spatrick using namespace lldb; 16*061da546Spatrick using namespace lldb_private; 17*061da546Spatrick 18*061da546Spatrick void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, 19*061da546Spatrick uint32_t dump_mask) { 20*061da546Spatrick const Type array_element_type = ConvertTypeMaskToType(m_type_mask); 21*061da546Spatrick if (dump_mask & eDumpOptionType) { 22*061da546Spatrick if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) 23*061da546Spatrick strm.Printf("(%s of %ss)", GetTypeAsCString(), 24*061da546Spatrick GetBuiltinTypeAsCString(array_element_type)); 25*061da546Spatrick else 26*061da546Spatrick strm.Printf("(%s)", GetTypeAsCString()); 27*061da546Spatrick } 28*061da546Spatrick if (dump_mask & eDumpOptionValue) { 29*061da546Spatrick const bool one_line = dump_mask & eDumpOptionCommand; 30*061da546Spatrick const uint32_t size = m_values.size(); 31*061da546Spatrick if (dump_mask & eDumpOptionType) 32*061da546Spatrick strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); 33*061da546Spatrick if (!one_line) 34*061da546Spatrick strm.IndentMore(); 35*061da546Spatrick for (uint32_t i = 0; i < size; ++i) { 36*061da546Spatrick if (!one_line) { 37*061da546Spatrick strm.Indent(); 38*061da546Spatrick strm.Printf("[%u]: ", i); 39*061da546Spatrick } 40*061da546Spatrick const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; 41*061da546Spatrick switch (array_element_type) { 42*061da546Spatrick default: 43*061da546Spatrick case eTypeArray: 44*061da546Spatrick case eTypeDictionary: 45*061da546Spatrick case eTypeProperties: 46*061da546Spatrick case eTypeFileSpecList: 47*061da546Spatrick case eTypePathMap: 48*061da546Spatrick m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); 49*061da546Spatrick break; 50*061da546Spatrick 51*061da546Spatrick case eTypeBoolean: 52*061da546Spatrick case eTypeChar: 53*061da546Spatrick case eTypeEnum: 54*061da546Spatrick case eTypeFileSpec: 55*061da546Spatrick case eTypeFormat: 56*061da546Spatrick case eTypeSInt64: 57*061da546Spatrick case eTypeString: 58*061da546Spatrick case eTypeUInt64: 59*061da546Spatrick case eTypeUUID: 60*061da546Spatrick // No need to show the type for dictionaries of simple items 61*061da546Spatrick m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | 62*061da546Spatrick extra_dump_options); 63*061da546Spatrick break; 64*061da546Spatrick } 65*061da546Spatrick 66*061da546Spatrick if (!one_line) { 67*061da546Spatrick if (i < (size - 1)) 68*061da546Spatrick strm.EOL(); 69*061da546Spatrick } else { 70*061da546Spatrick strm << ' '; 71*061da546Spatrick } 72*061da546Spatrick } 73*061da546Spatrick if (!one_line) 74*061da546Spatrick strm.IndentLess(); 75*061da546Spatrick } 76*061da546Spatrick } 77*061da546Spatrick 78*061da546Spatrick Status OptionValueArray::SetValueFromString(llvm::StringRef value, 79*061da546Spatrick VarSetOperationType op) { 80*061da546Spatrick Args args(value.str()); 81*061da546Spatrick Status error = SetArgs(args, op); 82*061da546Spatrick if (error.Success()) 83*061da546Spatrick NotifyValueChanged(); 84*061da546Spatrick return error; 85*061da546Spatrick } 86*061da546Spatrick 87*061da546Spatrick lldb::OptionValueSP 88*061da546Spatrick OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx, 89*061da546Spatrick llvm::StringRef name, bool will_modify, 90*061da546Spatrick Status &error) const { 91*061da546Spatrick if (name.empty() || name.front() != '[') { 92*061da546Spatrick error.SetErrorStringWithFormat( 93*061da546Spatrick "invalid value path '%s', %s values only support '[<index>]' subvalues " 94*061da546Spatrick "where <index> is a positive or negative array index", 95*061da546Spatrick name.str().c_str(), GetTypeAsCString()); 96*061da546Spatrick return nullptr; 97*061da546Spatrick } 98*061da546Spatrick 99*061da546Spatrick name = name.drop_front(); 100*061da546Spatrick llvm::StringRef index, sub_value; 101*061da546Spatrick std::tie(index, sub_value) = name.split(']'); 102*061da546Spatrick if (index.size() == name.size()) { 103*061da546Spatrick // Couldn't find a closing bracket 104*061da546Spatrick return nullptr; 105*061da546Spatrick } 106*061da546Spatrick 107*061da546Spatrick const size_t array_count = m_values.size(); 108*061da546Spatrick int32_t idx = 0; 109*061da546Spatrick if (index.getAsInteger(0, idx)) 110*061da546Spatrick return nullptr; 111*061da546Spatrick 112*061da546Spatrick uint32_t new_idx = UINT32_MAX; 113*061da546Spatrick if (idx < 0) { 114*061da546Spatrick // Access from the end of the array if the index is negative 115*061da546Spatrick new_idx = array_count - idx; 116*061da546Spatrick } else { 117*061da546Spatrick // Just a standard index 118*061da546Spatrick new_idx = idx; 119*061da546Spatrick } 120*061da546Spatrick 121*061da546Spatrick if (new_idx < array_count) { 122*061da546Spatrick if (m_values[new_idx]) { 123*061da546Spatrick if (!sub_value.empty()) 124*061da546Spatrick return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, 125*061da546Spatrick will_modify, error); 126*061da546Spatrick else 127*061da546Spatrick return m_values[new_idx]; 128*061da546Spatrick } 129*061da546Spatrick } else { 130*061da546Spatrick if (array_count == 0) 131*061da546Spatrick error.SetErrorStringWithFormat( 132*061da546Spatrick "index %i is not valid for an empty array", idx); 133*061da546Spatrick else if (idx > 0) 134*061da546Spatrick error.SetErrorStringWithFormat( 135*061da546Spatrick "index %i out of range, valid values are 0 through %" PRIu64, 136*061da546Spatrick idx, (uint64_t)(array_count - 1)); 137*061da546Spatrick else 138*061da546Spatrick error.SetErrorStringWithFormat("negative index %i out of range, " 139*061da546Spatrick "valid values are -1 through " 140*061da546Spatrick "-%" PRIu64, 141*061da546Spatrick idx, (uint64_t)array_count); 142*061da546Spatrick } 143*061da546Spatrick return OptionValueSP(); 144*061da546Spatrick } 145*061da546Spatrick 146*061da546Spatrick size_t OptionValueArray::GetArgs(Args &args) const { 147*061da546Spatrick args.Clear(); 148*061da546Spatrick const uint32_t size = m_values.size(); 149*061da546Spatrick for (uint32_t i = 0; i < size; ++i) { 150*061da546Spatrick llvm::StringRef string_value = m_values[i]->GetStringValue(); 151*061da546Spatrick if (!string_value.empty()) 152*061da546Spatrick args.AppendArgument(string_value); 153*061da546Spatrick } 154*061da546Spatrick 155*061da546Spatrick return args.GetArgumentCount(); 156*061da546Spatrick } 157*061da546Spatrick 158*061da546Spatrick Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { 159*061da546Spatrick Status error; 160*061da546Spatrick const size_t argc = args.GetArgumentCount(); 161*061da546Spatrick switch (op) { 162*061da546Spatrick case eVarSetOperationInvalid: 163*061da546Spatrick error.SetErrorString("unsupported operation"); 164*061da546Spatrick break; 165*061da546Spatrick 166*061da546Spatrick case eVarSetOperationInsertBefore: 167*061da546Spatrick case eVarSetOperationInsertAfter: 168*061da546Spatrick if (argc > 1) { 169*061da546Spatrick uint32_t idx = 170*061da546Spatrick StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 171*061da546Spatrick const uint32_t count = GetSize(); 172*061da546Spatrick if (idx > count) { 173*061da546Spatrick error.SetErrorStringWithFormat( 174*061da546Spatrick "invalid insert array index %u, index must be 0 through %u", idx, 175*061da546Spatrick count); 176*061da546Spatrick } else { 177*061da546Spatrick if (op == eVarSetOperationInsertAfter) 178*061da546Spatrick ++idx; 179*061da546Spatrick for (size_t i = 1; i < argc; ++i, ++idx) { 180*061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 181*061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 182*061da546Spatrick if (value_sp) { 183*061da546Spatrick if (error.Fail()) 184*061da546Spatrick return error; 185*061da546Spatrick if (idx >= m_values.size()) 186*061da546Spatrick m_values.push_back(value_sp); 187*061da546Spatrick else 188*061da546Spatrick m_values.insert(m_values.begin() + idx, value_sp); 189*061da546Spatrick } else { 190*061da546Spatrick error.SetErrorString( 191*061da546Spatrick "array of complex types must subclass OptionValueArray"); 192*061da546Spatrick return error; 193*061da546Spatrick } 194*061da546Spatrick } 195*061da546Spatrick } 196*061da546Spatrick } else { 197*061da546Spatrick error.SetErrorString("insert operation takes an array index followed by " 198*061da546Spatrick "one or more values"); 199*061da546Spatrick } 200*061da546Spatrick break; 201*061da546Spatrick 202*061da546Spatrick case eVarSetOperationRemove: 203*061da546Spatrick if (argc > 0) { 204*061da546Spatrick const uint32_t size = m_values.size(); 205*061da546Spatrick std::vector<int> remove_indexes; 206*061da546Spatrick bool all_indexes_valid = true; 207*061da546Spatrick size_t i; 208*061da546Spatrick for (i = 0; i < argc; ++i) { 209*061da546Spatrick const size_t idx = 210*061da546Spatrick StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX); 211*061da546Spatrick if (idx >= size) { 212*061da546Spatrick all_indexes_valid = false; 213*061da546Spatrick break; 214*061da546Spatrick } else 215*061da546Spatrick remove_indexes.push_back(idx); 216*061da546Spatrick } 217*061da546Spatrick 218*061da546Spatrick if (all_indexes_valid) { 219*061da546Spatrick size_t num_remove_indexes = remove_indexes.size(); 220*061da546Spatrick if (num_remove_indexes) { 221*061da546Spatrick // Sort and then erase in reverse so indexes are always valid 222*061da546Spatrick if (num_remove_indexes > 1) { 223*061da546Spatrick llvm::sort(remove_indexes.begin(), remove_indexes.end()); 224*061da546Spatrick for (std::vector<int>::const_reverse_iterator 225*061da546Spatrick pos = remove_indexes.rbegin(), 226*061da546Spatrick end = remove_indexes.rend(); 227*061da546Spatrick pos != end; ++pos) { 228*061da546Spatrick m_values.erase(m_values.begin() + *pos); 229*061da546Spatrick } 230*061da546Spatrick } else { 231*061da546Spatrick // Only one index 232*061da546Spatrick m_values.erase(m_values.begin() + remove_indexes.front()); 233*061da546Spatrick } 234*061da546Spatrick } 235*061da546Spatrick } else { 236*061da546Spatrick error.SetErrorStringWithFormat( 237*061da546Spatrick "invalid array index '%s', aborting remove operation", 238*061da546Spatrick args.GetArgumentAtIndex(i)); 239*061da546Spatrick } 240*061da546Spatrick } else { 241*061da546Spatrick error.SetErrorString("remove operation takes one or more array indices"); 242*061da546Spatrick } 243*061da546Spatrick break; 244*061da546Spatrick 245*061da546Spatrick case eVarSetOperationClear: 246*061da546Spatrick Clear(); 247*061da546Spatrick break; 248*061da546Spatrick 249*061da546Spatrick case eVarSetOperationReplace: 250*061da546Spatrick if (argc > 1) { 251*061da546Spatrick uint32_t idx = 252*061da546Spatrick StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX); 253*061da546Spatrick const uint32_t count = GetSize(); 254*061da546Spatrick if (idx > count) { 255*061da546Spatrick error.SetErrorStringWithFormat( 256*061da546Spatrick "invalid replace array index %u, index must be 0 through %u", idx, 257*061da546Spatrick count); 258*061da546Spatrick } else { 259*061da546Spatrick for (size_t i = 1; i < argc; ++i, ++idx) { 260*061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 261*061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 262*061da546Spatrick if (value_sp) { 263*061da546Spatrick if (error.Fail()) 264*061da546Spatrick return error; 265*061da546Spatrick if (idx < count) 266*061da546Spatrick m_values[idx] = value_sp; 267*061da546Spatrick else 268*061da546Spatrick m_values.push_back(value_sp); 269*061da546Spatrick } else { 270*061da546Spatrick error.SetErrorString( 271*061da546Spatrick "array of complex types must subclass OptionValueArray"); 272*061da546Spatrick return error; 273*061da546Spatrick } 274*061da546Spatrick } 275*061da546Spatrick } 276*061da546Spatrick } else { 277*061da546Spatrick error.SetErrorString("replace operation takes an array index followed by " 278*061da546Spatrick "one or more values"); 279*061da546Spatrick } 280*061da546Spatrick break; 281*061da546Spatrick 282*061da546Spatrick case eVarSetOperationAssign: 283*061da546Spatrick m_values.clear(); 284*061da546Spatrick // Fall through to append case 285*061da546Spatrick LLVM_FALLTHROUGH; 286*061da546Spatrick case eVarSetOperationAppend: 287*061da546Spatrick for (size_t i = 0; i < argc; ++i) { 288*061da546Spatrick lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 289*061da546Spatrick args.GetArgumentAtIndex(i), m_type_mask, error)); 290*061da546Spatrick if (value_sp) { 291*061da546Spatrick if (error.Fail()) 292*061da546Spatrick return error; 293*061da546Spatrick m_value_was_set = true; 294*061da546Spatrick AppendValue(value_sp); 295*061da546Spatrick } else { 296*061da546Spatrick error.SetErrorString( 297*061da546Spatrick "array of complex types must subclass OptionValueArray"); 298*061da546Spatrick } 299*061da546Spatrick } 300*061da546Spatrick break; 301*061da546Spatrick } 302*061da546Spatrick return error; 303*061da546Spatrick } 304*061da546Spatrick 305*061da546Spatrick lldb::OptionValueSP OptionValueArray::DeepCopy() const { 306*061da546Spatrick OptionValueArray *copied_array = 307*061da546Spatrick new OptionValueArray(m_type_mask, m_raw_value_dump); 308*061da546Spatrick lldb::OptionValueSP copied_value_sp(copied_array); 309*061da546Spatrick *static_cast<OptionValue *>(copied_array) = *this; 310*061da546Spatrick copied_array->m_callback = m_callback; 311*061da546Spatrick const uint32_t size = m_values.size(); 312*061da546Spatrick for (uint32_t i = 0; i < size; ++i) { 313*061da546Spatrick copied_array->AppendValue(m_values[i]->DeepCopy()); 314*061da546Spatrick } 315*061da546Spatrick return copied_value_sp; 316*061da546Spatrick } 317