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 
23 OptionValueProperties::OptionValueProperties(ConstString name)
24     : OptionValue(), m_name(name), m_properties(), m_name_to_index() {}
25 
26 OptionValueProperties::OptionValueProperties(
27     const OptionValueProperties &global_properties)
28     : OptionValue(global_properties),
29       std::enable_shared_from_this<OptionValueProperties>(),
30       m_name(global_properties.m_name),
31       m_properties(global_properties.m_properties),
32       m_name_to_index(global_properties.m_name_to_index) {
33   // We now have an exact copy of "global_properties". We need to now find all
34   // non-global settings and copy the property values so that all non-global
35   // settings get new OptionValue instances created for them.
36   const size_t num_properties = m_properties.size();
37   for (size_t i = 0; i < num_properties; ++i) {
38     // Duplicate any values that are not global when constructing properties
39     // from a global copy
40     if (!m_properties[i].IsGlobal()) {
41       lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy());
42       m_properties[i].SetOptionValue(new_value_sp);
43     }
44   }
45 }
46 
47 size_t OptionValueProperties::GetNumProperties() const {
48   return m_properties.size();
49 }
50 
51 void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {
52   for (const auto &definition : defs) {
53     Property property(definition);
54     assert(property.IsValid());
55     m_name_to_index.Append(ConstString(property.GetName()), m_properties.size());
56     property.GetValue()->SetParent(shared_from_this());
57     m_properties.push_back(property);
58   }
59   m_name_to_index.Sort();
60 }
61 
62 void OptionValueProperties::SetValueChangedCallback(
63     uint32_t property_idx, std::function<void()> callback) {
64   Property *property = ProtectedGetPropertyAtIndex(property_idx);
65   if (property)
66     property->SetValueChangedCallback(std::move(callback));
67 }
68 
69 void OptionValueProperties::AppendProperty(ConstString name,
70                                            ConstString desc,
71                                            bool is_global,
72                                            const OptionValueSP &value_sp) {
73   Property property(name, desc, is_global, value_sp);
74   m_name_to_index.Append(name, m_properties.size());
75   m_properties.push_back(property);
76   value_sp->SetParent(shared_from_this());
77   m_name_to_index.Sort();
78 }
79 
80 // bool
81 // OptionValueProperties::GetQualifiedName (Stream &strm)
82 //{
83 //    bool dumped_something = false;
84 ////    lldb::OptionValuePropertiesSP parent_sp(GetParent ());
85 ////    if (parent_sp)
86 ////    {
87 ////        parent_sp->GetQualifiedName (strm);
88 ////        strm.PutChar('.');
89 ////        dumped_something = true;
90 ////    }
91 //    if (m_name)
92 //    {
93 //        strm << m_name;
94 //        dumped_something = true;
95 //    }
96 //    return dumped_something;
97 //}
98 //
99 lldb::OptionValueSP
100 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
101                                       ConstString key,
102                                       bool will_modify) const {
103   lldb::OptionValueSP value_sp;
104   size_t idx = m_name_to_index.Find(key, SIZE_MAX);
105   if (idx < m_properties.size())
106     value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue();
107   return value_sp;
108 }
109 
110 lldb::OptionValueSP
111 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
112                                    llvm::StringRef name, bool will_modify,
113                                    Status &error) const {
114   lldb::OptionValueSP value_sp;
115   if (name.empty())
116     return OptionValueSP();
117 
118   llvm::StringRef sub_name;
119   ConstString key;
120   size_t key_len = name.find_first_of(".[{");
121   if (key_len != llvm::StringRef::npos) {
122     key.SetString(name.take_front(key_len));
123     sub_name = name.drop_front(key_len);
124   } else
125     key.SetString(name);
126 
127   value_sp = GetValueForKey(exe_ctx, key, will_modify);
128   if (sub_name.empty() || !value_sp)
129     return value_sp;
130 
131   switch (sub_name[0]) {
132   case '.': {
133     lldb::OptionValueSP return_val_sp;
134     return_val_sp =
135         value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error);
136     if (!return_val_sp) {
137       if (Properties::IsSettingExperimental(sub_name.drop_front())) {
138         size_t experimental_len =
139             strlen(Properties::GetExperimentalSettingsName());
140         if (sub_name[experimental_len + 1] == '.')
141           return_val_sp = value_sp->GetSubValue(
142               exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error);
143         // It isn't an error if an experimental setting is not present.
144         if (!return_val_sp)
145           error.Clear();
146       }
147     }
148     return return_val_sp;
149   }
150   case '[':
151     // Array or dictionary access for subvalues like: "[12]"       -- access
152     // 12th array element "['hello']"  -- dictionary access of key named hello
153     return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
154 
155   default:
156     value_sp.reset();
157     break;
158   }
159   return value_sp;
160 }
161 
162 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
163                                           VarSetOperationType op,
164                                           llvm::StringRef name,
165                                           llvm::StringRef value) {
166   Status error;
167   const bool will_modify = true;
168   llvm::SmallVector<llvm::StringRef, 8> components;
169   name.split(components, '.');
170   bool name_contains_experimental = false;
171   for (const auto &part : components)
172     if (Properties::IsSettingExperimental(part))
173       name_contains_experimental = true;
174 
175   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
176   if (value_sp)
177     error = value_sp->SetValueFromString(value, op);
178   else {
179     // Don't set an error if the path contained .experimental. - those are
180     // allowed to be missing and should silently fail.
181     if (!name_contains_experimental && error.AsCString() == nullptr) {
182       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
183     }
184   }
185   return error;
186 }
187 
188 uint32_t
189 OptionValueProperties::GetPropertyIndex(ConstString name) const {
190   return m_name_to_index.Find(name, SIZE_MAX);
191 }
192 
193 const Property *
194 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
195                                    bool will_modify,
196                                    ConstString name) const {
197   return GetPropertyAtIndex(
198       exe_ctx, will_modify,
199       m_name_to_index.Find(name, SIZE_MAX));
200 }
201 
202 const Property *OptionValueProperties::GetPropertyAtIndex(
203     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
204   return ProtectedGetPropertyAtIndex(idx);
205 }
206 
207 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
208     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
209   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
210   if (setting)
211     return setting->GetValue();
212   return OptionValueSP();
213 }
214 
215 OptionValuePathMappings *
216 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
217     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
218   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
219   if (value_sp)
220     return value_sp->GetAsPathMappings();
221   return nullptr;
222 }
223 
224 OptionValueFileSpecList *
225 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
226     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
227   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
228   if (value_sp)
229     return value_sp->GetAsFileSpecList();
230   return nullptr;
231 }
232 
233 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
234     const ExecutionContext *exe_ctx, uint32_t idx) const {
235   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
236   if (property)
237     return property->GetValue()->GetAsArch();
238   return nullptr;
239 }
240 
241 OptionValueLanguage *
242 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
243     const ExecutionContext *exe_ctx, uint32_t idx) const {
244   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
245   if (property)
246     return property->GetValue()->GetAsLanguage();
247   return nullptr;
248 }
249 
250 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
251     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
252   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
253   if (property) {
254     OptionValue *value = property->GetValue().get();
255     if (value) {
256       const OptionValueArray *array = value->GetAsArray();
257       if (array)
258         return array->GetArgs(args);
259       else {
260         const OptionValueDictionary *dict = value->GetAsDictionary();
261         if (dict)
262           return dict->GetArgs(args);
263       }
264     }
265   }
266   return false;
267 }
268 
269 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
270     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
271   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
272   if (property) {
273     OptionValue *value = property->GetValue().get();
274     if (value) {
275       OptionValueArray *array = value->GetAsArray();
276       if (array)
277         return array->SetArgs(args, eVarSetOperationAssign).Success();
278       else {
279         OptionValueDictionary *dict = value->GetAsDictionary();
280         if (dict)
281           return dict->SetArgs(args, eVarSetOperationAssign).Success();
282       }
283     }
284   }
285   return false;
286 }
287 
288 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
289     const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
290   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
291   if (property) {
292     OptionValue *value = property->GetValue().get();
293     if (value)
294       return value->GetBooleanValue(fail_value);
295   }
296   return fail_value;
297 }
298 
299 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
300     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
301   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
302   if (property) {
303     OptionValue *value = property->GetValue().get();
304     if (value) {
305       value->SetBooleanValue(new_value);
306       return true;
307     }
308   }
309   return false;
310 }
311 
312 OptionValueDictionary *
313 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
314     const ExecutionContext *exe_ctx, uint32_t idx) const {
315   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
316   if (property)
317     return property->GetValue()->GetAsDictionary();
318   return nullptr;
319 }
320 
321 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
322     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
323   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
324   if (property) {
325     OptionValue *value = property->GetValue().get();
326     if (value)
327       return value->GetEnumerationValue(fail_value);
328   }
329   return fail_value;
330 }
331 
332 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
333     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
334   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
335   if (property) {
336     OptionValue *value = property->GetValue().get();
337     if (value)
338       return value->SetEnumerationValue(new_value);
339   }
340   return false;
341 }
342 
343 const FormatEntity::Entry *
344 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
345     const ExecutionContext *exe_ctx, uint32_t idx) {
346   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
347   if (property) {
348     OptionValue *value = property->GetValue().get();
349     if (value)
350       return value->GetFormatEntity();
351   }
352   return nullptr;
353 }
354 
355 OptionValueFileSpec *
356 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
357     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
358   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
359   if (property) {
360     OptionValue *value = property->GetValue().get();
361     if (value)
362       return value->GetAsFileSpec();
363   }
364   return nullptr;
365 }
366 
367 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
368     const ExecutionContext *exe_ctx, uint32_t idx) const {
369   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
370   if (property) {
371     OptionValue *value = property->GetValue().get();
372     if (value)
373       return value->GetFileSpecValue();
374   }
375   return FileSpec();
376 }
377 
378 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
379     const ExecutionContext *exe_ctx, uint32_t idx,
380     const FileSpec &new_file_spec) {
381   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
382   if (property) {
383     OptionValue *value = property->GetValue().get();
384     if (value)
385       return value->SetFileSpecValue(new_file_spec);
386   }
387   return false;
388 }
389 
390 const RegularExpression *
391 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
392     const ExecutionContext *exe_ctx, uint32_t idx) const {
393   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
394   if (property) {
395     OptionValue *value = property->GetValue().get();
396     if (value)
397       return value->GetRegexValue();
398   }
399   return nullptr;
400 }
401 
402 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
403     const ExecutionContext *exe_ctx, uint32_t idx) const {
404   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
405   if (property) {
406     OptionValue *value = property->GetValue().get();
407     if (value)
408       return value->GetAsSInt64();
409   }
410   return nullptr;
411 }
412 
413 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
414     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
415   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
416   if (property) {
417     OptionValue *value = property->GetValue().get();
418     if (value)
419       return value->GetSInt64Value(fail_value);
420   }
421   return fail_value;
422 }
423 
424 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
425     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
426   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
427   if (property) {
428     OptionValue *value = property->GetValue().get();
429     if (value)
430       return value->SetSInt64Value(new_value);
431   }
432   return false;
433 }
434 
435 llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString(
436     const ExecutionContext *exe_ctx, uint32_t idx,
437     llvm::StringRef fail_value) const {
438   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
439   if (property) {
440     OptionValue *value = property->GetValue().get();
441     if (value)
442       return value->GetStringValue(fail_value);
443   }
444   return fail_value;
445 }
446 
447 bool OptionValueProperties::SetPropertyAtIndexAsString(
448     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
449   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
450   if (property) {
451     OptionValue *value = property->GetValue().get();
452     if (value)
453       return value->SetStringValue(new_value);
454   }
455   return false;
456 }
457 
458 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
459     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
460   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
461   if (value_sp)
462     return value_sp->GetAsString();
463   return nullptr;
464 }
465 
466 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
467     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
468   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
469   if (property) {
470     OptionValue *value = property->GetValue().get();
471     if (value)
472       return value->GetUInt64Value(fail_value);
473   }
474   return fail_value;
475 }
476 
477 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
478     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
479   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
480   if (property) {
481     OptionValue *value = property->GetValue().get();
482     if (value)
483       return value->SetUInt64Value(new_value);
484   }
485   return false;
486 }
487 
488 void OptionValueProperties::Clear() {
489   const size_t num_properties = m_properties.size();
490   for (size_t i = 0; i < num_properties; ++i)
491     m_properties[i].GetValue()->Clear();
492 }
493 
494 Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
495                                                  VarSetOperationType op) {
496   Status error;
497 
498   //    Args args(value_cstr);
499   //    const size_t argc = args.GetArgumentCount();
500   switch (op) {
501   case eVarSetOperationClear:
502     Clear();
503     break;
504 
505   case eVarSetOperationReplace:
506   case eVarSetOperationAssign:
507   case eVarSetOperationRemove:
508   case eVarSetOperationInsertBefore:
509   case eVarSetOperationInsertAfter:
510   case eVarSetOperationAppend:
511   case eVarSetOperationInvalid:
512     error = OptionValue::SetValueFromString(value, op);
513     break;
514   }
515 
516   return error;
517 }
518 
519 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
520                                       Stream &strm, uint32_t dump_mask) {
521   const size_t num_properties = m_properties.size();
522   for (size_t i = 0; i < num_properties; ++i) {
523     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
524     if (property) {
525       OptionValue *option_value = property->GetValue().get();
526       assert(option_value);
527       const bool transparent_value = option_value->ValueIsTransparent();
528       property->Dump(exe_ctx, strm, dump_mask);
529       if (!transparent_value)
530         strm.EOL();
531     }
532   }
533 }
534 
535 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
536                                                 Stream &strm,
537                                                 llvm::StringRef property_path,
538                                                 uint32_t dump_mask) {
539   Status error;
540   const bool will_modify = false;
541   lldb::OptionValueSP value_sp(
542       GetSubValue(exe_ctx, property_path, will_modify, error));
543   if (value_sp) {
544     if (!value_sp->ValueIsTransparent()) {
545       if (dump_mask & eDumpOptionName)
546         strm.PutCString(property_path);
547       if (dump_mask & ~eDumpOptionName)
548         strm.PutChar(' ');
549     }
550     value_sp->DumpValue(exe_ctx, strm, dump_mask);
551   }
552   return error;
553 }
554 
555 lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
556   llvm_unreachable("this shouldn't happen");
557 }
558 
559 const Property *OptionValueProperties::GetPropertyAtPath(
560     const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const {
561   const Property *property = nullptr;
562   if (name.empty())
563     return nullptr;
564   llvm::StringRef sub_name;
565   ConstString key;
566   size_t key_len = name.find_first_of(".[{");
567 
568   if (key_len != llvm::StringRef::npos) {
569     key.SetString(name.take_front(key_len));
570     sub_name = name.drop_front(key_len);
571   } else
572     key.SetString(name);
573 
574   property = GetProperty(exe_ctx, will_modify, key);
575   if (sub_name.empty() || !property)
576     return property;
577 
578   if (sub_name[0] == '.') {
579     OptionValueProperties *sub_properties =
580         property->GetValue()->GetAsProperties();
581     if (sub_properties)
582       return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
583                                                 sub_name.drop_front());
584   }
585   return nullptr;
586 }
587 
588 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
589                                                 Stream &strm) const {
590   size_t max_name_len = 0;
591   const size_t num_properties = m_properties.size();
592   for (size_t i = 0; i < num_properties; ++i) {
593     const Property *property = ProtectedGetPropertyAtIndex(i);
594     if (property)
595       max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
596   }
597   for (size_t i = 0; i < num_properties; ++i) {
598     const Property *property = ProtectedGetPropertyAtIndex(i);
599     if (property)
600       property->DumpDescription(interpreter, strm, max_name_len, false);
601   }
602 }
603 
604 void OptionValueProperties::Apropos(
605     llvm::StringRef keyword,
606     std::vector<const Property *> &matching_properties) const {
607   const size_t num_properties = m_properties.size();
608   StreamString strm;
609   for (size_t i = 0; i < num_properties; ++i) {
610     const Property *property = ProtectedGetPropertyAtIndex(i);
611     if (property) {
612       const OptionValueProperties *properties =
613           property->GetValue()->GetAsProperties();
614       if (properties) {
615         properties->Apropos(keyword, matching_properties);
616       } else {
617         bool match = false;
618         llvm::StringRef name = property->GetName();
619         if (name.contains_lower(keyword))
620           match = true;
621         else {
622           llvm::StringRef desc = property->GetDescription();
623           if (desc.contains_lower(keyword))
624             match = true;
625         }
626         if (match) {
627           matching_properties.push_back(property);
628         }
629       }
630     }
631   }
632 }
633 
634 lldb::OptionValuePropertiesSP
635 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
636                                       ConstString name) {
637   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
638   if (option_value_sp) {
639     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
640     if (ov_properties)
641       return ov_properties->shared_from_this();
642   }
643   return lldb::OptionValuePropertiesSP();
644 }
645