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