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(llvm::StringRef name)
24     : m_name(name.str()) {}
25 
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 
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 
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
53 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
65 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 
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 
141 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 *
149 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 
157 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 *
166 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 *
175 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 
183 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 
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 *
240 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 *
249 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 
260 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 
271 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 
282 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 
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 
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 
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
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 
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
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
387 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 *
406 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 
435 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 
451 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
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