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