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/Utility/Args.h" 12 #include "lldb/Utility/Stream.h" 13 14 using namespace lldb; 15 using namespace lldb_private; 16 17 void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, 18 uint32_t dump_mask) { 19 const Type array_element_type = ConvertTypeMaskToType(m_type_mask); 20 if (dump_mask & eDumpOptionType) { 21 if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) 22 strm.Printf("(%s of %ss)", GetTypeAsCString(), 23 GetBuiltinTypeAsCString(array_element_type)); 24 else 25 strm.Printf("(%s)", GetTypeAsCString()); 26 } 27 if (dump_mask & eDumpOptionValue) { 28 const bool one_line = dump_mask & eDumpOptionCommand; 29 const uint32_t size = m_values.size(); 30 if (dump_mask & eDumpOptionType) 31 strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); 32 if (!one_line) 33 strm.IndentMore(); 34 for (uint32_t i = 0; i < size; ++i) { 35 if (!one_line) { 36 strm.Indent(); 37 strm.Printf("[%u]: ", i); 38 } 39 const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; 40 switch (array_element_type) { 41 default: 42 case eTypeArray: 43 case eTypeDictionary: 44 case eTypeProperties: 45 case eTypeFileSpecList: 46 case eTypePathMap: 47 m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); 48 break; 49 50 case eTypeBoolean: 51 case eTypeChar: 52 case eTypeEnum: 53 case eTypeFileSpec: 54 case eTypeFileLineColumn: 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 const uint32_t count = GetSize(); 171 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 172 error.SetErrorStringWithFormat( 173 "invalid insert array index %s, index must be 0 through %u", 174 args.GetArgumentAtIndex(0), count); 175 } else { 176 if (op == eVarSetOperationInsertAfter) 177 ++idx; 178 for (size_t i = 1; i < argc; ++i, ++idx) { 179 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 180 args.GetArgumentAtIndex(i), m_type_mask, error)); 181 if (value_sp) { 182 if (error.Fail()) 183 return error; 184 if (idx >= m_values.size()) 185 m_values.push_back(value_sp); 186 else 187 m_values.insert(m_values.begin() + idx, value_sp); 188 } else { 189 error.SetErrorString( 190 "array of complex types must subclass OptionValueArray"); 191 return error; 192 } 193 } 194 } 195 } else { 196 error.SetErrorString("insert operation takes an array index followed by " 197 "one or more values"); 198 } 199 break; 200 201 case eVarSetOperationRemove: 202 if (argc > 0) { 203 const uint32_t size = m_values.size(); 204 std::vector<int> remove_indexes; 205 bool all_indexes_valid = true; 206 size_t i; 207 for (i = 0; i < argc; ++i) { 208 size_t idx; 209 if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) { 210 all_indexes_valid = false; 211 break; 212 } else 213 remove_indexes.push_back(idx); 214 } 215 216 if (all_indexes_valid) { 217 size_t num_remove_indexes = remove_indexes.size(); 218 if (num_remove_indexes) { 219 // Sort and then erase in reverse so indexes are always valid 220 if (num_remove_indexes > 1) { 221 llvm::sort(remove_indexes); 222 for (std::vector<int>::const_reverse_iterator 223 pos = remove_indexes.rbegin(), 224 end = remove_indexes.rend(); 225 pos != end; ++pos) { 226 m_values.erase(m_values.begin() + *pos); 227 } 228 } else { 229 // Only one index 230 m_values.erase(m_values.begin() + remove_indexes.front()); 231 } 232 } 233 } else { 234 error.SetErrorStringWithFormat( 235 "invalid array index '%s', aborting remove operation", 236 args.GetArgumentAtIndex(i)); 237 } 238 } else { 239 error.SetErrorString("remove operation takes one or more array indices"); 240 } 241 break; 242 243 case eVarSetOperationClear: 244 Clear(); 245 break; 246 247 case eVarSetOperationReplace: 248 if (argc > 1) { 249 uint32_t idx; 250 const uint32_t count = GetSize(); 251 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 252 error.SetErrorStringWithFormat( 253 "invalid replace array index %s, index must be 0 through %u", 254 args.GetArgumentAtIndex(0), count); 255 } else { 256 for (size_t i = 1; i < argc; ++i, ++idx) { 257 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 258 args.GetArgumentAtIndex(i), m_type_mask, error)); 259 if (value_sp) { 260 if (error.Fail()) 261 return error; 262 if (idx < count) 263 m_values[idx] = value_sp; 264 else 265 m_values.push_back(value_sp); 266 } else { 267 error.SetErrorString( 268 "array of complex types must subclass OptionValueArray"); 269 return error; 270 } 271 } 272 } 273 } else { 274 error.SetErrorString("replace operation takes an array index followed by " 275 "one or more values"); 276 } 277 break; 278 279 case eVarSetOperationAssign: 280 m_values.clear(); 281 // Fall through to append case 282 LLVM_FALLTHROUGH; 283 case eVarSetOperationAppend: 284 for (size_t i = 0; i < argc; ++i) { 285 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 286 args.GetArgumentAtIndex(i), m_type_mask, error)); 287 if (value_sp) { 288 if (error.Fail()) 289 return error; 290 m_value_was_set = true; 291 AppendValue(value_sp); 292 } else { 293 error.SetErrorString( 294 "array of complex types must subclass OptionValueArray"); 295 } 296 } 297 break; 298 } 299 return error; 300 } 301 302 OptionValueSP 303 OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { 304 auto copy_sp = OptionValue::DeepCopy(new_parent); 305 // copy_sp->GetAsArray cannot be used here as it doesn't work for derived 306 // types that override GetType returning a different value. 307 auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); 308 lldbassert(array_value_ptr); 309 310 for (auto &value : array_value_ptr->m_values) 311 value = value->DeepCopy(copy_sp); 312 313 return copy_sp; 314 } 315