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