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