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
20 using namespace lldb;
21 using namespace lldb_private;
22
OptionValueProperties(llvm::StringRef name)23 OptionValueProperties::OptionValueProperties(llvm::StringRef name)
24 : m_name(name.str()) {}
25
Initialize(const PropertyDefinitions & defs)26 void 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({property.GetName(), m_properties.size()});
31 property.GetValue()->SetParent(shared_from_this());
32 m_properties.push_back(property);
33 }
34 }
35
SetValueChangedCallback(size_t property_idx,std::function<void ()> callback)36 void OptionValueProperties::SetValueChangedCallback(
37 size_t property_idx, std::function<void()> callback) {
38 Property *property = ProtectedGetPropertyAtIndex(property_idx);
39 if (property)
40 property->SetValueChangedCallback(std::move(callback));
41 }
42
AppendProperty(llvm::StringRef name,llvm::StringRef desc,bool is_global,const OptionValueSP & value_sp)43 void 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({name, m_properties.size()});
48 m_properties.push_back(property);
49 value_sp->SetParent(shared_from_this());
50 }
51
52 lldb::OptionValueSP
GetValueForKey(const ExecutionContext * exe_ctx,llvm::StringRef key) const53 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
54 llvm::StringRef key) const {
55 auto iter = m_name_to_index.find(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
64 lldb::OptionValueSP
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,Status & error) const65 OptionValueProperties::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(".[{");
74 if (key_len != llvm::StringRef::npos) {
75 key = name.take_front(key_len);
76 sub_name = name.drop_front(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, sub_name.drop_front(), error);
89 if (!return_val_sp) {
90 if (Properties::IsSettingExperimental(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, sub_name.drop_front(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, sub_name, error);
107
108 default:
109 value_sp.reset();
110 break;
111 }
112 return value_sp;
113 }
114
SetSubValue(const ExecutionContext * exe_ctx,VarSetOperationType op,llvm::StringRef name,llvm::StringRef value)115 Status 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(components, '.');
122 bool name_contains_experimental = false;
123 for (const auto &part : components)
124 if (Properties::IsSettingExperimental(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
GetPropertyIndex(llvm::StringRef name) const141 size_t OptionValueProperties::GetPropertyIndex(llvm::StringRef name) const {
142 auto iter = m_name_to_index.find(name);
143 if (iter == m_name_to_index.end())
144 return SIZE_MAX;
145 return iter->second;
146 }
147
148 const Property *
GetProperty(llvm::StringRef name,const ExecutionContext * exe_ctx) const149 OptionValueProperties::GetProperty(llvm::StringRef name,
150 const ExecutionContext *exe_ctx) const {
151 auto iter = m_name_to_index.find(name);
152 if (iter == m_name_to_index.end())
153 return nullptr;
154 return GetPropertyAtIndex(iter->second, exe_ctx);
155 }
156
GetPropertyValueAtIndex(size_t idx,const ExecutionContext * exe_ctx) const157 lldb::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
165 OptionValuePathMappings *
GetPropertyAtIndexAsOptionValuePathMappings(size_t idx,const ExecutionContext * exe_ctx) const166 OptionValueProperties::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
174 OptionValueFileSpecList *
GetPropertyAtIndexAsOptionValueFileSpecList(size_t idx,const ExecutionContext * exe_ctx) const175 OptionValueProperties::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
GetPropertyAtIndexAsArgs(size_t idx,Args & args,const ExecutionContext * exe_ctx) const183 bool 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
SetPropertyAtIndexFromArgs(size_t idx,const Args & args,const ExecutionContext * exe_ctx)214 bool 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, eVarSetOperationAssign).Success();
227
228 OptionValueArray *array = value->GetAsArray();
229 if (array)
230 return array->SetArgs(args, eVarSetOperationAssign).Success();
231
232 OptionValueDictionary *dict = value->GetAsDictionary();
233 if (dict)
234 return dict->SetArgs(args, eVarSetOperationAssign).Success();
235
236 return false;
237 }
238
239 OptionValueDictionary *
GetPropertyAtIndexAsOptionValueDictionary(size_t idx,const ExecutionContext * exe_ctx) const240 OptionValueProperties::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
248 OptionValueFileSpec *
GetPropertyAtIndexAsOptionValueFileSpec(size_t idx,const ExecutionContext * exe_ctx) const249 OptionValueProperties::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
GetPropertyAtIndexAsOptionValueSInt64(size_t idx,const ExecutionContext * exe_ctx) const260 OptionValueSInt64 *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
GetPropertyAtIndexAsOptionValueUInt64(size_t idx,const ExecutionContext * exe_ctx) const271 OptionValueUInt64 *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
GetPropertyAtIndexAsOptionValueString(size_t idx,const ExecutionContext * exe_ctx) const282 OptionValueString *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
Clear()290 void 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
SetValueFromString(llvm::StringRef value,VarSetOperationType op)296 Status 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
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)321 void 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(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
337 llvm::json::Value
ToJSON(const ExecutionContext * exe_ctx)338 OptionValueProperties::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(i, exe_ctx);
343 if (property) {
344 OptionValue *option_value = property->GetValue().get();
345 assert(option_value);
346 json_properties.try_emplace(property->GetName(),
347 option_value->ToJSON(exe_ctx));
348 }
349 }
350 return json_properties;
351 }
352
DumpPropertyValue(const ExecutionContext * exe_ctx,Stream & strm,llvm::StringRef property_path,uint32_t dump_mask,bool is_json)353 Status 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, property_path, error));
360 if (value_sp) {
361 if (!value_sp->ValueIsTransparent()) {
362 if (dump_mask & eDumpOptionName)
363 strm.PutCString(property_path);
364 if (dump_mask & ~eDumpOptionName)
365 strm.PutChar(' ');
366 }
367 if (is_json) {
368 strm.Printf(
369 "%s",
370 llvm::formatv("{0:2}", 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
377 OptionValuePropertiesSP
CreateLocalCopy(const Properties & global_properties)378 OptionValueProperties::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(global_props_sp->GetParent());
383 return std::static_pointer_cast<OptionValueProperties>(copy_sp);
384 }
385
386 OptionValueSP
DeepCopy(const OptionValueSP & new_parent) const387 OptionValueProperties::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(copy_sp);
399 property.SetOptionValue(value_sp);
400 }
401 }
402 return copy_sp;
403 }
404
405 const Property *
GetPropertyAtPath(const ExecutionContext * exe_ctx,llvm::StringRef name) const406 OptionValueProperties::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(".[{");
415
416 if (key_len != llvm::StringRef::npos) {
417 key = name.take_front(key_len);
418 sub_name = name.drop_front(key_len);
419 } else
420 key = name;
421
422 property = GetProperty(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, sub_name.drop_front());
431 }
432 return nullptr;
433 }
434
DumpAllDescriptions(CommandInterpreter & interpreter,Stream & strm) const435 void 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(i);
441 if (property)
442 max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
443 }
444 for (size_t i = 0; i < num_properties; ++i) {
445 const Property *property = ProtectedGetPropertyAtIndex(i);
446 if (property)
447 property->DumpDescription(interpreter, strm, max_name_len, false);
448 }
449 }
450
Apropos(llvm::StringRef keyword,std::vector<const Property * > & matching_properties) const451 void 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(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(keyword))
467 match = true;
468 else {
469 llvm::StringRef desc = property->GetDescription();
470 if (desc.contains_insensitive(keyword))
471 match = true;
472 }
473 if (match) {
474 matching_properties.push_back(property);
475 }
476 }
477 }
478 }
479 }
480
481 lldb::OptionValuePropertiesSP
GetSubProperty(const ExecutionContext * exe_ctx,llvm::StringRef name)482 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
483 llvm::StringRef name) {
484 lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, 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