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
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)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
SetValueFromString(llvm::StringRef value,VarSetOperationType op)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
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,bool will_modify,Status & error) const88 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
GetArgs(Args & args) const146 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
SetArgs(const Args & args,VarSetOperationType op)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
DeepCopy(const OptionValueSP & new_parent) const303 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