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 = Status::FromErrorStringWithFormat( 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 = Status::FromErrorStringWithFormat( 138 "index %i is not valid for an empty array", idx); 139 else if (idx > 0) 140 error = Status::FromErrorStringWithFormat( 141 "index %i out of range, valid values are 0 through %" PRIu64, idx, 142 (uint64_t)(array_count - 1)); 143 else 144 error = 145 Status::FromErrorStringWithFormat("negative index %i out of range, " 146 "valid values are -1 through " 147 "-%" PRIu64, 148 idx, (uint64_t)array_count); 149 } 150 return OptionValueSP(); 151 } 152 153 size_t OptionValueArray::GetArgs(Args &args) const { 154 args.Clear(); 155 const uint32_t size = m_values.size(); 156 for (uint32_t i = 0; i < size; ++i) { 157 auto string_value = m_values[i]->GetValueAs<llvm::StringRef>(); 158 if (string_value) 159 args.AppendArgument(*string_value); 160 } 161 162 return args.GetArgumentCount(); 163 } 164 165 Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { 166 Status error; 167 const size_t argc = args.GetArgumentCount(); 168 switch (op) { 169 case eVarSetOperationInvalid: 170 error = Status::FromErrorString("unsupported operation"); 171 break; 172 173 case eVarSetOperationInsertBefore: 174 case eVarSetOperationInsertAfter: 175 if (argc > 1) { 176 uint32_t idx; 177 const uint32_t count = GetSize(); 178 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 179 error = Status::FromErrorStringWithFormat( 180 "invalid insert array index %s, index must be 0 through %u", 181 args.GetArgumentAtIndex(0), count); 182 } else { 183 if (op == eVarSetOperationInsertAfter) 184 ++idx; 185 for (size_t i = 1; i < argc; ++i, ++idx) { 186 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 187 args.GetArgumentAtIndex(i), m_type_mask, error)); 188 if (value_sp) { 189 if (error.Fail()) 190 return error; 191 if (idx >= m_values.size()) 192 m_values.push_back(value_sp); 193 else 194 m_values.insert(m_values.begin() + idx, value_sp); 195 } else { 196 error = Status::FromErrorString( 197 "array of complex types must subclass OptionValueArray"); 198 return error; 199 } 200 } 201 } 202 } else { 203 error = Status::FromErrorString( 204 "insert operation takes an array index followed by " 205 "one or more values"); 206 } 207 break; 208 209 case eVarSetOperationRemove: 210 if (argc > 0) { 211 const uint32_t size = m_values.size(); 212 std::vector<int> remove_indexes; 213 bool all_indexes_valid = true; 214 size_t i; 215 for (i = 0; i < argc; ++i) { 216 size_t idx; 217 if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) { 218 all_indexes_valid = false; 219 break; 220 } else 221 remove_indexes.push_back(idx); 222 } 223 224 if (all_indexes_valid) { 225 size_t num_remove_indexes = remove_indexes.size(); 226 if (num_remove_indexes) { 227 // Sort and then erase in reverse so indexes are always valid 228 if (num_remove_indexes > 1) { 229 llvm::sort(remove_indexes); 230 for (std::vector<int>::const_reverse_iterator 231 pos = remove_indexes.rbegin(), 232 end = remove_indexes.rend(); 233 pos != end; ++pos) { 234 m_values.erase(m_values.begin() + *pos); 235 } 236 } else { 237 // Only one index 238 m_values.erase(m_values.begin() + remove_indexes.front()); 239 } 240 } 241 } else { 242 error = Status::FromErrorStringWithFormat( 243 "invalid array index '%s', aborting remove operation", 244 args.GetArgumentAtIndex(i)); 245 } 246 } else { 247 error = Status::FromErrorString( 248 "remove operation takes one or more array indices"); 249 } 250 break; 251 252 case eVarSetOperationClear: 253 Clear(); 254 break; 255 256 case eVarSetOperationReplace: 257 if (argc > 1) { 258 uint32_t idx; 259 const uint32_t count = GetSize(); 260 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 261 error = Status::FromErrorStringWithFormat( 262 "invalid replace array index %s, index must be 0 through %u", 263 args.GetArgumentAtIndex(0), count); 264 } else { 265 for (size_t i = 1; i < argc; ++i, ++idx) { 266 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 267 args.GetArgumentAtIndex(i), m_type_mask, error)); 268 if (value_sp) { 269 if (error.Fail()) 270 return error; 271 if (idx < count) 272 m_values[idx] = value_sp; 273 else 274 m_values.push_back(value_sp); 275 } else { 276 error = Status::FromErrorString( 277 "array of complex types must subclass OptionValueArray"); 278 return error; 279 } 280 } 281 } 282 } else { 283 error = Status::FromErrorString( 284 "replace operation takes an array index followed by " 285 "one or more values"); 286 } 287 break; 288 289 case eVarSetOperationAssign: 290 m_values.clear(); 291 // Fall through to append case 292 [[fallthrough]]; 293 case eVarSetOperationAppend: 294 for (size_t i = 0; i < argc; ++i) { 295 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 296 args.GetArgumentAtIndex(i), m_type_mask, error)); 297 if (value_sp) { 298 if (error.Fail()) 299 return error; 300 m_value_was_set = true; 301 AppendValue(value_sp); 302 } else { 303 error = Status::FromErrorString( 304 "array of complex types must subclass OptionValueArray"); 305 } 306 } 307 break; 308 } 309 return error; 310 } 311 312 OptionValueSP 313 OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { 314 auto copy_sp = OptionValue::DeepCopy(new_parent); 315 // copy_sp->GetAsArray cannot be used here as it doesn't work for derived 316 // types that override GetType returning a different value. 317 auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); 318 lldbassert(array_value_ptr); 319 320 for (auto &value : array_value_ptr->m_values) 321 value = value->DeepCopy(copy_sp); 322 323 return copy_sp; 324 } 325