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, bool will_modify, 98 Status &error) const { 99 if (name.empty() || name.front() != '[') { 100 error.SetErrorStringWithFormat( 101 "invalid value path '%s', %s values only support '[<index>]' subvalues " 102 "where <index> is a positive or negative array index", 103 name.str().c_str(), GetTypeAsCString()); 104 return nullptr; 105 } 106 107 name = name.drop_front(); 108 llvm::StringRef index, sub_value; 109 std::tie(index, sub_value) = name.split(']'); 110 if (index.size() == name.size()) { 111 // Couldn't find a closing bracket 112 return nullptr; 113 } 114 115 const size_t array_count = m_values.size(); 116 int32_t idx = 0; 117 if (index.getAsInteger(0, idx)) 118 return nullptr; 119 120 uint32_t new_idx = UINT32_MAX; 121 if (idx < 0) { 122 // Access from the end of the array if the index is negative 123 new_idx = array_count - idx; 124 } else { 125 // Just a standard index 126 new_idx = idx; 127 } 128 129 if (new_idx < array_count) { 130 if (m_values[new_idx]) { 131 if (!sub_value.empty()) 132 return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, 133 will_modify, error); 134 else 135 return m_values[new_idx]; 136 } 137 } else { 138 if (array_count == 0) 139 error.SetErrorStringWithFormat( 140 "index %i is not valid for an empty array", idx); 141 else if (idx > 0) 142 error.SetErrorStringWithFormat( 143 "index %i out of range, valid values are 0 through %" PRIu64, 144 idx, (uint64_t)(array_count - 1)); 145 else 146 error.SetErrorStringWithFormat("negative index %i out of range, " 147 "valid values are -1 through " 148 "-%" PRIu64, 149 idx, (uint64_t)array_count); 150 } 151 return OptionValueSP(); 152 } 153 154 size_t OptionValueArray::GetArgs(Args &args) const { 155 args.Clear(); 156 const uint32_t size = m_values.size(); 157 for (uint32_t i = 0; i < size; ++i) { 158 llvm::StringRef string_value = m_values[i]->GetStringValue(); 159 if (!string_value.empty()) 160 args.AppendArgument(string_value); 161 } 162 163 return args.GetArgumentCount(); 164 } 165 166 Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { 167 Status error; 168 const size_t argc = args.GetArgumentCount(); 169 switch (op) { 170 case eVarSetOperationInvalid: 171 error.SetErrorString("unsupported operation"); 172 break; 173 174 case eVarSetOperationInsertBefore: 175 case eVarSetOperationInsertAfter: 176 if (argc > 1) { 177 uint32_t idx; 178 const uint32_t count = GetSize(); 179 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 180 error.SetErrorStringWithFormat( 181 "invalid insert array index %s, index must be 0 through %u", 182 args.GetArgumentAtIndex(0), count); 183 } else { 184 if (op == eVarSetOperationInsertAfter) 185 ++idx; 186 for (size_t i = 1; i < argc; ++i, ++idx) { 187 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 188 args.GetArgumentAtIndex(i), m_type_mask, error)); 189 if (value_sp) { 190 if (error.Fail()) 191 return error; 192 if (idx >= m_values.size()) 193 m_values.push_back(value_sp); 194 else 195 m_values.insert(m_values.begin() + idx, value_sp); 196 } else { 197 error.SetErrorString( 198 "array of complex types must subclass OptionValueArray"); 199 return error; 200 } 201 } 202 } 203 } else { 204 error.SetErrorString("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.SetErrorStringWithFormat( 243 "invalid array index '%s', aborting remove operation", 244 args.GetArgumentAtIndex(i)); 245 } 246 } else { 247 error.SetErrorString("remove operation takes one or more array indices"); 248 } 249 break; 250 251 case eVarSetOperationClear: 252 Clear(); 253 break; 254 255 case eVarSetOperationReplace: 256 if (argc > 1) { 257 uint32_t idx; 258 const uint32_t count = GetSize(); 259 if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { 260 error.SetErrorStringWithFormat( 261 "invalid replace array index %s, index must be 0 through %u", 262 args.GetArgumentAtIndex(0), count); 263 } else { 264 for (size_t i = 1; i < argc; ++i, ++idx) { 265 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 266 args.GetArgumentAtIndex(i), m_type_mask, error)); 267 if (value_sp) { 268 if (error.Fail()) 269 return error; 270 if (idx < count) 271 m_values[idx] = value_sp; 272 else 273 m_values.push_back(value_sp); 274 } else { 275 error.SetErrorString( 276 "array of complex types must subclass OptionValueArray"); 277 return error; 278 } 279 } 280 } 281 } else { 282 error.SetErrorString("replace operation takes an array index followed by " 283 "one or more values"); 284 } 285 break; 286 287 case eVarSetOperationAssign: 288 m_values.clear(); 289 // Fall through to append case 290 [[fallthrough]]; 291 case eVarSetOperationAppend: 292 for (size_t i = 0; i < argc; ++i) { 293 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 294 args.GetArgumentAtIndex(i), m_type_mask, error)); 295 if (value_sp) { 296 if (error.Fail()) 297 return error; 298 m_value_was_set = true; 299 AppendValue(value_sp); 300 } else { 301 error.SetErrorString( 302 "array of complex types must subclass OptionValueArray"); 303 } 304 } 305 break; 306 } 307 return error; 308 } 309 310 OptionValueSP 311 OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { 312 auto copy_sp = OptionValue::DeepCopy(new_parent); 313 // copy_sp->GetAsArray cannot be used here as it doesn't work for derived 314 // types that override GetType returning a different value. 315 auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); 316 lldbassert(array_value_ptr); 317 318 for (auto &value : array_value_ptr->m_values) 319 value = value->DeepCopy(copy_sp); 320 321 return copy_sp; 322 } 323