1//===-- OptionValueProperties.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/OptionValueProperties.h"
10
11#include "lldb/Utility/Flags.h"
12
13#include "lldb/Core/UserSettingsController.h"
14#include "lldb/Interpreter/OptionValues.h"
15#include "lldb/Interpreter/Property.h"
16#include "lldb/Utility/Args.h"
17#include "lldb/Utility/Stream.h"
18#include "lldb/Utility/StringList.h"
19
20using namespace lldb;
21using namespace lldb_private;
22
23OptionValueProperties::OptionValueProperties(llvm::StringRef name)
24 : m_name(name.str()) {}
25
26void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {
27 for (const auto &definition : defs) {
28 Property property(definition);
29 assert(property.IsValid());
30 m_name_to_index.insert(KV: {property.GetName(), m_properties.size()});
31 property.GetValue()->SetParent(shared_from_this());
32 m_properties.push_back(x: property);
33 }
34}
35
36void OptionValueProperties::SetValueChangedCallback(
37 size_t property_idx, std::function<void()> callback) {
38 Property *property = ProtectedGetPropertyAtIndex(idx: property_idx);
39 if (property)
40 property->SetValueChangedCallback(std::move(callback));
41}
42
43void OptionValueProperties::AppendProperty(llvm::StringRef name,
44 llvm::StringRef desc, bool is_global,
45 const OptionValueSP &value_sp) {
46 Property property(name, desc, is_global, value_sp);
47 m_name_to_index.insert(KV: {name, m_properties.size()});
48 m_properties.push_back(x: property);
49 value_sp->SetParent(shared_from_this());
50}
51
52lldb::OptionValueSP
53OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
54 llvm::StringRef key) const {
55 auto iter = m_name_to_index.find(Key: key);
56 if (iter == m_name_to_index.end())
57 return OptionValueSP();
58 const size_t idx = iter->second;
59 if (idx >= m_properties.size())
60 return OptionValueSP();
61 return GetPropertyAtIndex(idx, exe_ctx)->GetValue();
62}
63
64lldb::OptionValueSP
65OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
66 llvm::StringRef name, Status &error) const {
67 lldb::OptionValueSP value_sp;
68 if (name.empty())
69 return OptionValueSP();
70
71 llvm::StringRef sub_name;
72 llvm::StringRef key;
73 size_t key_len = name.find_first_of(Chars: ".[{");
74 if (key_len != llvm::StringRef::npos) {
75 key = name.take_front(N: key_len);
76 sub_name = name.drop_front(N: key_len);
77 } else
78 key = name;
79
80 value_sp = GetValueForKey(exe_ctx, key);
81 if (sub_name.empty() || !value_sp)
82 return value_sp;
83
84 switch (sub_name[0]) {
85 case '.': {
86 lldb::OptionValueSP return_val_sp;
87 return_val_sp =
88 value_sp->GetSubValue(exe_ctx, name: sub_name.drop_front(), error);
89 if (!return_val_sp) {
90 if (Properties::IsSettingExperimental(setting: sub_name.drop_front())) {
91 const size_t experimental_len =
92 Properties::GetExperimentalSettingsName().size();
93 if (sub_name[experimental_len + 1] == '.')
94 return_val_sp = value_sp->GetSubValue(
95 exe_ctx, name: sub_name.drop_front(N: experimental_len + 2), error);
96 // It isn't an error if an experimental setting is not present.
97 if (!return_val_sp)
98 error.Clear();
99 }
100 }
101 return return_val_sp;
102 }
103 case '[':
104 // Array or dictionary access for subvalues like: "[12]" -- access
105 // 12th array element "['hello']" -- dictionary access of key named hello
106 return value_sp->GetSubValue(exe_ctx, name: sub_name, error);
107
108 default:
109 value_sp.reset();
110 break;
111 }
112 return value_sp;
113}
114
115Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
116 VarSetOperationType op,
117 llvm::StringRef name,
118 llvm::StringRef value) {
119 Status error;
120 llvm::SmallVector<llvm::StringRef, 8> components;
121 name.split(A&: components, Separator: '.');
122 bool name_contains_experimental = false;
123 for (const auto &part : components)
124 if (Properties::IsSettingExperimental(setting: part))
125 name_contains_experimental = true;
126
127 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error));
128 if (value_sp)
129 error = value_sp->SetValueFromString(value, op);
130 else {
131 // Don't set an error if the path contained .experimental. - those are
132 // allowed to be missing and should silently fail.
133 if (!name_contains_experimental && error.AsCString() == nullptr) {
134 error.SetErrorStringWithFormat("invalid value path '%s'",
135 name.str().c_str());
136 }
137 }
138 return error;
139}
140
141size_t OptionValueProperties::GetPropertyIndex(llvm::StringRef name) const {
142 auto iter = m_name_to_index.find(Key: name);
143 if (iter == m_name_to_index.end())
144 return SIZE_MAX;
145 return iter->second;
146}
147
148const Property *
149OptionValueProperties::GetProperty(llvm::StringRef name,
150 const ExecutionContext *exe_ctx) const {
151 auto iter = m_name_to_index.find(Key: name);
152 if (iter == m_name_to_index.end())
153 return nullptr;
154 return GetPropertyAtIndex(idx: iter->second, exe_ctx);
155}
156
157lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
158 size_t idx, const ExecutionContext *exe_ctx) const {
159 const Property *setting = GetPropertyAtIndex(idx, exe_ctx);
160 if (setting)
161 return setting->GetValue();
162 return OptionValueSP();
163}
164
165OptionValuePathMappings *
166OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
167 size_t idx, const ExecutionContext *exe_ctx) const {
168 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));
169 if (value_sp)
170 return value_sp->GetAsPathMappings();
171 return nullptr;
172}
173
174OptionValueFileSpecList *
175OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
176 size_t idx, const ExecutionContext *exe_ctx) const {
177 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));
178 if (value_sp)
179 return value_sp->GetAsFileSpecList();
180 return nullptr;
181}
182
183bool OptionValueProperties::GetPropertyAtIndexAsArgs(
184 size_t idx, Args &args, const ExecutionContext *exe_ctx) const {
185 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
186 if (!property)
187 return false;
188
189 OptionValue *value = property->GetValue().get();
190 if (!value)
191 return false;
192
193 const OptionValueArgs *arguments = value->GetAsArgs();
194 if (arguments) {
195 arguments->GetArgs(args);
196 return true;
197 }
198
199 const OptionValueArray *array = value->GetAsArray();
200 if (array) {
201 array->GetArgs(args);
202 return true;
203 }
204
205 const OptionValueDictionary *dict = value->GetAsDictionary();
206 if (dict) {
207 dict->GetArgs(args);
208 return true;
209 }
210
211 return false;
212}
213
214bool OptionValueProperties::SetPropertyAtIndexFromArgs(
215 size_t idx, const Args &args, const ExecutionContext *exe_ctx) {
216 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
217 if (!property)
218 return false;
219
220 OptionValue *value = property->GetValue().get();
221 if (!value)
222 return false;
223
224 OptionValueArgs *arguments = value->GetAsArgs();
225 if (arguments)
226 return arguments->SetArgs(args, op: eVarSetOperationAssign).Success();
227
228 OptionValueArray *array = value->GetAsArray();
229 if (array)
230 return array->SetArgs(args, op: eVarSetOperationAssign).Success();
231
232 OptionValueDictionary *dict = value->GetAsDictionary();
233 if (dict)
234 return dict->SetArgs(args, op: eVarSetOperationAssign).Success();
235
236 return false;
237}
238
239OptionValueDictionary *
240OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
241 size_t idx, const ExecutionContext *exe_ctx) const {
242 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
243 if (property)
244 return property->GetValue()->GetAsDictionary();
245 return nullptr;
246}
247
248OptionValueFileSpec *
249OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
250 size_t idx, const ExecutionContext *exe_ctx) const {
251 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
252 if (property) {
253 OptionValue *value = property->GetValue().get();
254 if (value)
255 return value->GetAsFileSpec();
256 }
257 return nullptr;
258}
259
260OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
261 size_t idx, const ExecutionContext *exe_ctx) const {
262 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
263 if (property) {
264 OptionValue *value = property->GetValue().get();
265 if (value)
266 return value->GetAsSInt64();
267 }
268 return nullptr;
269}
270
271OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64(
272 size_t idx, const ExecutionContext *exe_ctx) const {
273 const Property *property = GetPropertyAtIndex(idx, exe_ctx);
274 if (property) {
275 OptionValue *value = property->GetValue().get();
276 if (value)
277 return value->GetAsUInt64();
278 }
279 return nullptr;
280}
281
282OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
283 size_t idx, const ExecutionContext *exe_ctx) const {
284 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));
285 if (value_sp)
286 return value_sp->GetAsString();
287 return nullptr;
288}
289
290void OptionValueProperties::Clear() {
291 const size_t num_properties = m_properties.size();
292 for (size_t i = 0; i < num_properties; ++i)
293 m_properties[i].GetValue()->Clear();
294}
295
296Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
297 VarSetOperationType op) {
298 Status error;
299
300 // Args args(value_cstr);
301 // const size_t argc = args.GetArgumentCount();
302 switch (op) {
303 case eVarSetOperationClear:
304 Clear();
305 break;
306
307 case eVarSetOperationReplace:
308 case eVarSetOperationAssign:
309 case eVarSetOperationRemove:
310 case eVarSetOperationInsertBefore:
311 case eVarSetOperationInsertAfter:
312 case eVarSetOperationAppend:
313 case eVarSetOperationInvalid:
314 error = OptionValue::SetValueFromString(value, op);
315 break;
316 }
317
318 return error;
319}
320
321void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
322 Stream &strm, uint32_t dump_mask) {
323 const size_t num_properties = m_properties.size();
324 for (size_t i = 0; i < num_properties; ++i) {
325 const Property *property = GetPropertyAtIndex(idx: i, exe_ctx);
326 if (property) {
327 OptionValue *option_value = property->GetValue().get();
328 assert(option_value);
329 const bool transparent_value = option_value->ValueIsTransparent();
330 property->Dump(exe_ctx, strm, dump_mask);
331 if (!transparent_value)
332 strm.EOL();
333 }
334 }
335}
336
337llvm::json::Value
338OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) {
339 llvm::json::Object json_properties;
340 const size_t num_properties = m_properties.size();
341 for (size_t i = 0; i < num_properties; ++i) {
342 const Property *property = GetPropertyAtIndex(idx: i, exe_ctx);
343 if (property) {
344 OptionValue *option_value = property->GetValue().get();
345 assert(option_value);
346 json_properties.try_emplace(K: property->GetName(),
347 Args: option_value->ToJSON(exe_ctx));
348 }
349 }
350 return json_properties;
351}
352
353Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
354 Stream &strm,
355 llvm::StringRef property_path,
356 uint32_t dump_mask,
357 bool is_json) {
358 Status error;
359 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name: property_path, error));
360 if (value_sp) {
361 if (!value_sp->ValueIsTransparent()) {
362 if (dump_mask & eDumpOptionName)
363 strm.PutCString(cstr: property_path);
364 if (dump_mask & ~eDumpOptionName)
365 strm.PutChar(ch: ' ');
366 }
367 if (is_json) {
368 strm.Printf(
369 format: "%s",
370 llvm::formatv(Fmt: "{0:2}", Vals: value_sp->ToJSON(exe_ctx)).str().c_str());
371 } else
372 value_sp->DumpValue(exe_ctx, strm, dump_mask);
373 }
374 return error;
375}
376
377OptionValuePropertiesSP
378OptionValueProperties::CreateLocalCopy(const Properties &global_properties) {
379 auto global_props_sp = global_properties.GetValueProperties();
380 lldbassert(global_props_sp);
381
382 auto copy_sp = global_props_sp->DeepCopy(new_parent: global_props_sp->GetParent());
383 return std::static_pointer_cast<OptionValueProperties>(r: copy_sp);
384}
385
386OptionValueSP
387OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const {
388 auto copy_sp = OptionValue::DeepCopy(new_parent);
389 // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived
390 // types that override GetType returning a different value.
391 auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get());
392 lldbassert(props_value_ptr);
393
394 for (auto &property : props_value_ptr->m_properties) {
395 // Duplicate any values that are not global when constructing properties
396 // from a global copy.
397 if (!property.IsGlobal()) {
398 auto value_sp = property.GetValue()->DeepCopy(new_parent: copy_sp);
399 property.SetOptionValue(value_sp);
400 }
401 }
402 return copy_sp;
403}
404
405const Property *
406OptionValueProperties::GetPropertyAtPath(const ExecutionContext *exe_ctx,
407 llvm::StringRef name) const {
408 if (name.empty())
409 return nullptr;
410
411 const Property *property = nullptr;
412 llvm::StringRef sub_name;
413 llvm::StringRef key;
414 size_t key_len = name.find_first_of(Chars: ".[{");
415
416 if (key_len != llvm::StringRef::npos) {
417 key = name.take_front(N: key_len);
418 sub_name = name.drop_front(N: key_len);
419 } else
420 key = name;
421
422 property = GetProperty(name: key, exe_ctx);
423 if (sub_name.empty() || !property)
424 return property;
425
426 if (sub_name[0] == '.') {
427 OptionValueProperties *sub_properties =
428 property->GetValue()->GetAsProperties();
429 if (sub_properties)
430 return sub_properties->GetPropertyAtPath(exe_ctx, name: sub_name.drop_front());
431 }
432 return nullptr;
433}
434
435void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
436 Stream &strm) const {
437 size_t max_name_len = 0;
438 const size_t num_properties = m_properties.size();
439 for (size_t i = 0; i < num_properties; ++i) {
440 const Property *property = ProtectedGetPropertyAtIndex(idx: i);
441 if (property)
442 max_name_len = std::max<size_t>(a: property->GetName().size(), b: max_name_len);
443 }
444 for (size_t i = 0; i < num_properties; ++i) {
445 const Property *property = ProtectedGetPropertyAtIndex(idx: i);
446 if (property)
447 property->DumpDescription(interpreter, strm, output_width: max_name_len, display_qualified_name: false);
448 }
449}
450
451void OptionValueProperties::Apropos(
452 llvm::StringRef keyword,
453 std::vector<const Property *> &matching_properties) const {
454 const size_t num_properties = m_properties.size();
455 StreamString strm;
456 for (size_t i = 0; i < num_properties; ++i) {
457 const Property *property = ProtectedGetPropertyAtIndex(idx: i);
458 if (property) {
459 const OptionValueProperties *properties =
460 property->GetValue()->GetAsProperties();
461 if (properties) {
462 properties->Apropos(keyword, matching_properties);
463 } else {
464 bool match = false;
465 llvm::StringRef name = property->GetName();
466 if (name.contains_insensitive(Other: keyword))
467 match = true;
468 else {
469 llvm::StringRef desc = property->GetDescription();
470 if (desc.contains_insensitive(Other: keyword))
471 match = true;
472 }
473 if (match) {
474 matching_properties.push_back(x: property);
475 }
476 }
477 }
478 }
479}
480
481lldb::OptionValuePropertiesSP
482OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
483 llvm::StringRef name) {
484 lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, key: name));
485 if (option_value_sp) {
486 OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
487 if (ov_properties)
488 return ov_properties->shared_from_this();
489 }
490 return lldb::OptionValuePropertiesSP();
491}
492

source code of lldb/source/Interpreter/OptionValueProperties.cpp