1 //===-- OptionValueProperties.cpp --------------------------------*- C++-*-===//
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(const 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, OptionValueChangedCallback callback, void *baton) {
64   Property *property = ProtectedGetPropertyAtIndex(property_idx);
65   if (property)
66     property->SetValueChangedCallback(callback, baton);
67 }
68 
69 void OptionValueProperties::AppendProperty(const ConstString &name,
70                                            const 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                                       const 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     // Predicate matching for predicates like
152     // "<setting-name>{<predicate>}"
153     // strings are parsed by the current OptionValueProperties subclass to mean
154     // whatever they want to. For instance a subclass of OptionValueProperties
155     // for a lldb_private::Target might implement: "target.run-
156     // args{arch==i386}"   -- only set run args if the arch is i386 "target
157     // .run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the path
158     // matches "target.run-args{basename==test&&arch==x86_64}" -- only set run
159     // args if executable basename is "test" and arch is "x86_64"
160     if (sub_name[1]) {
161       llvm::StringRef predicate_start = sub_name.drop_front();
162       size_t pos = predicate_start.find_first_of('}');
163       if (pos != llvm::StringRef::npos) {
164         auto predicate = predicate_start.take_front(pos);
165         auto rest = predicate_start.drop_front(pos);
166         if (PredicateMatches(exe_ctx, predicate)) {
167           if (!rest.empty()) {
168             // Still more subvalue string to evaluate
169             return value_sp->GetSubValue(exe_ctx, rest,
170                                           will_modify, error);
171           } else {
172             // We have a match!
173             break;
174           }
175         }
176       }
177     }
178     // Predicate didn't match or wasn't correctly formed
179     value_sp.reset();
180     break;
181 
182   case '[':
183     // Array or dictionary access for subvalues like: "[12]"       -- access
184     // 12th array element "['hello']"  -- dictionary access of key named hello
185     return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
186 
187   default:
188     value_sp.reset();
189     break;
190   }
191   return value_sp;
192 }
193 
194 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
195                                           VarSetOperationType op,
196                                           llvm::StringRef name,
197                                           llvm::StringRef value) {
198   Status error;
199   const bool will_modify = true;
200   llvm::SmallVector<llvm::StringRef, 8> components;
201   name.split(components, '.');
202   bool name_contains_experimental = false;
203   for (const auto &part : components)
204     if (Properties::IsSettingExperimental(part))
205       name_contains_experimental = true;
206 
207 
208   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
209   if (value_sp)
210     error = value_sp->SetValueFromString(value, op);
211   else {
212     // Don't set an error if the path contained .experimental. - those are
213     // allowed to be missing and should silently fail.
214     if (!name_contains_experimental && error.AsCString() == nullptr) {
215       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
216     }
217   }
218   return error;
219 }
220 
221 uint32_t
222 OptionValueProperties::GetPropertyIndex(const ConstString &name) const {
223   return m_name_to_index.Find(name, SIZE_MAX);
224 }
225 
226 const Property *
227 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
228                                    bool will_modify,
229                                    const ConstString &name) const {
230   return GetPropertyAtIndex(
231       exe_ctx, will_modify,
232       m_name_to_index.Find(name, SIZE_MAX));
233 }
234 
235 const Property *OptionValueProperties::GetPropertyAtIndex(
236     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
237   return ProtectedGetPropertyAtIndex(idx);
238 }
239 
240 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
241     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
242   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
243   if (setting)
244     return setting->GetValue();
245   return OptionValueSP();
246 }
247 
248 OptionValuePathMappings *
249 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
250     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
251   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
252   if (value_sp)
253     return value_sp->GetAsPathMappings();
254   return nullptr;
255 }
256 
257 OptionValueFileSpecList *
258 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
259     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
260   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
261   if (value_sp)
262     return value_sp->GetAsFileSpecList();
263   return nullptr;
264 }
265 
266 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
267     const ExecutionContext *exe_ctx, uint32_t idx) const {
268   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
269   if (property)
270     return property->GetValue()->GetAsArch();
271   return nullptr;
272 }
273 
274 OptionValueLanguage *
275 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
276     const ExecutionContext *exe_ctx, uint32_t idx) const {
277   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
278   if (property)
279     return property->GetValue()->GetAsLanguage();
280   return nullptr;
281 }
282 
283 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
284     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
285   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
286   if (property) {
287     OptionValue *value = property->GetValue().get();
288     if (value) {
289       const OptionValueArray *array = value->GetAsArray();
290       if (array)
291         return array->GetArgs(args);
292       else {
293         const OptionValueDictionary *dict = value->GetAsDictionary();
294         if (dict)
295           return dict->GetArgs(args);
296       }
297     }
298   }
299   return false;
300 }
301 
302 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
303     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
304   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
305   if (property) {
306     OptionValue *value = property->GetValue().get();
307     if (value) {
308       OptionValueArray *array = value->GetAsArray();
309       if (array)
310         return array->SetArgs(args, eVarSetOperationAssign).Success();
311       else {
312         OptionValueDictionary *dict = value->GetAsDictionary();
313         if (dict)
314           return dict->SetArgs(args, eVarSetOperationAssign).Success();
315       }
316     }
317   }
318   return false;
319 }
320 
321 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
322     const ExecutionContext *exe_ctx, uint32_t idx, bool 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->GetBooleanValue(fail_value);
328   }
329   return fail_value;
330 }
331 
332 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
333     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
334   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
335   if (property) {
336     OptionValue *value = property->GetValue().get();
337     if (value) {
338       value->SetBooleanValue(new_value);
339       return true;
340     }
341   }
342   return false;
343 }
344 
345 OptionValueDictionary *
346 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
347     const ExecutionContext *exe_ctx, uint32_t idx) const {
348   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
349   if (property)
350     return property->GetValue()->GetAsDictionary();
351   return nullptr;
352 }
353 
354 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
355     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) 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->GetEnumerationValue(fail_value);
361   }
362   return fail_value;
363 }
364 
365 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
366     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
367   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
368   if (property) {
369     OptionValue *value = property->GetValue().get();
370     if (value)
371       return value->SetEnumerationValue(new_value);
372   }
373   return false;
374 }
375 
376 const FormatEntity::Entry *
377 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
378     const ExecutionContext *exe_ctx, uint32_t idx) {
379   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
380   if (property) {
381     OptionValue *value = property->GetValue().get();
382     if (value)
383       return value->GetFormatEntity();
384   }
385   return nullptr;
386 }
387 
388 OptionValueFileSpec *
389 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
390     const ExecutionContext *exe_ctx, bool will_modify, 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->GetAsFileSpec();
396   }
397   return nullptr;
398 }
399 
400 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
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->GetFileSpecValue();
407   }
408   return FileSpec();
409 }
410 
411 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
412     const ExecutionContext *exe_ctx, uint32_t idx,
413     const FileSpec &new_file_spec) {
414   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
415   if (property) {
416     OptionValue *value = property->GetValue().get();
417     if (value)
418       return value->SetFileSpecValue(new_file_spec);
419   }
420   return false;
421 }
422 
423 const RegularExpression *
424 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
425     const ExecutionContext *exe_ctx, uint32_t idx) const {
426   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
427   if (property) {
428     OptionValue *value = property->GetValue().get();
429     if (value)
430       return value->GetRegexValue();
431   }
432   return nullptr;
433 }
434 
435 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
436     const ExecutionContext *exe_ctx, uint32_t idx) const {
437   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
438   if (property) {
439     OptionValue *value = property->GetValue().get();
440     if (value)
441       return value->GetAsSInt64();
442   }
443   return nullptr;
444 }
445 
446 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
447     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
448   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
449   if (property) {
450     OptionValue *value = property->GetValue().get();
451     if (value)
452       return value->GetSInt64Value(fail_value);
453   }
454   return fail_value;
455 }
456 
457 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
458     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
459   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
460   if (property) {
461     OptionValue *value = property->GetValue().get();
462     if (value)
463       return value->SetSInt64Value(new_value);
464   }
465   return false;
466 }
467 
468 llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString(
469     const ExecutionContext *exe_ctx, uint32_t idx,
470     llvm::StringRef fail_value) const {
471   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
472   if (property) {
473     OptionValue *value = property->GetValue().get();
474     if (value)
475       return value->GetStringValue(fail_value);
476   }
477   return fail_value;
478 }
479 
480 bool OptionValueProperties::SetPropertyAtIndexAsString(
481     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
482   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
483   if (property) {
484     OptionValue *value = property->GetValue().get();
485     if (value)
486       return value->SetStringValue(new_value);
487   }
488   return false;
489 }
490 
491 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
492     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
493   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
494   if (value_sp)
495     return value_sp->GetAsString();
496   return nullptr;
497 }
498 
499 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
500     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
501   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
502   if (property) {
503     OptionValue *value = property->GetValue().get();
504     if (value)
505       return value->GetUInt64Value(fail_value);
506   }
507   return fail_value;
508 }
509 
510 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
511     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
512   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
513   if (property) {
514     OptionValue *value = property->GetValue().get();
515     if (value)
516       return value->SetUInt64Value(new_value);
517   }
518   return false;
519 }
520 
521 bool OptionValueProperties::Clear() {
522   const size_t num_properties = m_properties.size();
523   for (size_t i = 0; i < num_properties; ++i)
524     m_properties[i].GetValue()->Clear();
525   return true;
526 }
527 
528 Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
529                                                  VarSetOperationType op) {
530   Status error;
531 
532   //    Args args(value_cstr);
533   //    const size_t argc = args.GetArgumentCount();
534   switch (op) {
535   case eVarSetOperationClear:
536     Clear();
537     break;
538 
539   case eVarSetOperationReplace:
540   case eVarSetOperationAssign:
541   case eVarSetOperationRemove:
542   case eVarSetOperationInsertBefore:
543   case eVarSetOperationInsertAfter:
544   case eVarSetOperationAppend:
545   case eVarSetOperationInvalid:
546     error = OptionValue::SetValueFromString(value, op);
547     break;
548   }
549 
550   return error;
551 }
552 
553 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
554                                       Stream &strm, uint32_t dump_mask) {
555   const size_t num_properties = m_properties.size();
556   for (size_t i = 0; i < num_properties; ++i) {
557     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
558     if (property) {
559       OptionValue *option_value = property->GetValue().get();
560       assert(option_value);
561       const bool transparent_value = option_value->ValueIsTransparent();
562       property->Dump(exe_ctx, strm, dump_mask);
563       if (!transparent_value)
564         strm.EOL();
565     }
566   }
567 }
568 
569 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
570                                                 Stream &strm,
571                                                 llvm::StringRef property_path,
572                                                 uint32_t dump_mask) {
573   Status error;
574   const bool will_modify = false;
575   lldb::OptionValueSP value_sp(
576       GetSubValue(exe_ctx, property_path, will_modify, error));
577   if (value_sp) {
578     if (!value_sp->ValueIsTransparent()) {
579       if (dump_mask & eDumpOptionName)
580         strm.PutCString(property_path);
581       if (dump_mask & ~eDumpOptionName)
582         strm.PutChar(' ');
583     }
584     value_sp->DumpValue(exe_ctx, strm, dump_mask);
585   }
586   return error;
587 }
588 
589 lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
590   llvm_unreachable("this shouldn't happen");
591 }
592 
593 const Property *OptionValueProperties::GetPropertyAtPath(
594     const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const {
595   const Property *property = nullptr;
596   if (name.empty())
597     return nullptr;
598   llvm::StringRef sub_name;
599   ConstString key;
600   size_t key_len = name.find_first_of(".[{");
601 
602   if (key_len != llvm::StringRef::npos) {
603     key.SetString(name.take_front(key_len));
604     sub_name = name.drop_front(key_len);
605   } else
606     key.SetString(name);
607 
608   property = GetProperty(exe_ctx, will_modify, key);
609   if (sub_name.empty() || !property)
610     return property;
611 
612   if (sub_name[0] == '.') {
613     OptionValueProperties *sub_properties =
614         property->GetValue()->GetAsProperties();
615     if (sub_properties)
616       return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
617                                                 sub_name.drop_front());
618   }
619   return nullptr;
620 }
621 
622 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
623                                                 Stream &strm) const {
624   size_t max_name_len = 0;
625   const size_t num_properties = m_properties.size();
626   for (size_t i = 0; i < num_properties; ++i) {
627     const Property *property = ProtectedGetPropertyAtIndex(i);
628     if (property)
629       max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
630   }
631   for (size_t i = 0; i < num_properties; ++i) {
632     const Property *property = ProtectedGetPropertyAtIndex(i);
633     if (property)
634       property->DumpDescription(interpreter, strm, max_name_len, false);
635   }
636 }
637 
638 void OptionValueProperties::Apropos(
639     llvm::StringRef keyword,
640     std::vector<const Property *> &matching_properties) const {
641   const size_t num_properties = m_properties.size();
642   StreamString strm;
643   for (size_t i = 0; i < num_properties; ++i) {
644     const Property *property = ProtectedGetPropertyAtIndex(i);
645     if (property) {
646       const OptionValueProperties *properties =
647           property->GetValue()->GetAsProperties();
648       if (properties) {
649         properties->Apropos(keyword, matching_properties);
650       } else {
651         bool match = false;
652         llvm::StringRef name = property->GetName();
653         if (name.contains_lower(keyword))
654           match = true;
655         else {
656           llvm::StringRef desc = property->GetDescription();
657           if (desc.contains_lower(keyword))
658             match = true;
659         }
660         if (match) {
661           matching_properties.push_back(property);
662         }
663       }
664     }
665   }
666 }
667 
668 lldb::OptionValuePropertiesSP
669 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
670                                       const ConstString &name) {
671   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
672   if (option_value_sp) {
673     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
674     if (ov_properties)
675       return ov_properties->shared_from_this();
676   }
677   return lldb::OptionValuePropertiesSP();
678 }
679