xref: /openbsd-src/gnu/llvm/lldb/source/Interpreter/OptionValueDictionary.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- OptionValueDictionary.cpp -----------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Interpreter/OptionValueDictionary.h"
10061da546Spatrick 
11061da546Spatrick #include "lldb/DataFormatters/FormatManager.h"
12*f6aab3d8Srobert #include "lldb/Interpreter/OptionValueEnumeration.h"
13061da546Spatrick #include "lldb/Interpreter/OptionValueString.h"
14061da546Spatrick #include "lldb/Utility/Args.h"
15061da546Spatrick #include "lldb/Utility/State.h"
16*f6aab3d8Srobert #include "llvm/ADT/StringRef.h"
17061da546Spatrick 
18061da546Spatrick using namespace lldb;
19061da546Spatrick using namespace lldb_private;
20061da546Spatrick 
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)21061da546Spatrick void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
22061da546Spatrick                                       Stream &strm, uint32_t dump_mask) {
23061da546Spatrick   const Type dict_type = ConvertTypeMaskToType(m_type_mask);
24061da546Spatrick   if (dump_mask & eDumpOptionType) {
25061da546Spatrick     if (m_type_mask != eTypeInvalid)
26061da546Spatrick       strm.Printf("(%s of %ss)", GetTypeAsCString(),
27061da546Spatrick                   GetBuiltinTypeAsCString(dict_type));
28061da546Spatrick     else
29061da546Spatrick       strm.Printf("(%s)", GetTypeAsCString());
30061da546Spatrick   }
31061da546Spatrick   if (dump_mask & eDumpOptionValue) {
32061da546Spatrick     const bool one_line = dump_mask & eDumpOptionCommand;
33061da546Spatrick     if (dump_mask & eDumpOptionType)
34061da546Spatrick       strm.PutCString(" =");
35061da546Spatrick 
36061da546Spatrick     collection::iterator pos, end = m_values.end();
37061da546Spatrick 
38061da546Spatrick     if (!one_line)
39061da546Spatrick       strm.IndentMore();
40061da546Spatrick 
41061da546Spatrick     for (pos = m_values.begin(); pos != end; ++pos) {
42061da546Spatrick       OptionValue *option_value = pos->second.get();
43061da546Spatrick 
44061da546Spatrick       if (one_line)
45061da546Spatrick         strm << ' ';
46061da546Spatrick       else
47061da546Spatrick         strm.EOL();
48061da546Spatrick 
49dda28197Spatrick       strm.Indent(pos->first.GetStringRef());
50061da546Spatrick 
51061da546Spatrick       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
52061da546Spatrick       switch (dict_type) {
53061da546Spatrick       default:
54061da546Spatrick       case eTypeArray:
55061da546Spatrick       case eTypeDictionary:
56061da546Spatrick       case eTypeProperties:
57061da546Spatrick       case eTypeFileSpecList:
58061da546Spatrick       case eTypePathMap:
59061da546Spatrick         strm.PutChar(' ');
60061da546Spatrick         option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
61061da546Spatrick         break;
62061da546Spatrick 
63061da546Spatrick       case eTypeBoolean:
64061da546Spatrick       case eTypeChar:
65061da546Spatrick       case eTypeEnum:
66be691f3bSpatrick       case eTypeFileLineColumn:
67061da546Spatrick       case eTypeFileSpec:
68061da546Spatrick       case eTypeFormat:
69061da546Spatrick       case eTypeSInt64:
70061da546Spatrick       case eTypeString:
71061da546Spatrick       case eTypeUInt64:
72061da546Spatrick       case eTypeUUID:
73061da546Spatrick         // No need to show the type for dictionaries of simple items
74061da546Spatrick         strm.PutCString("=");
75061da546Spatrick         option_value->DumpValue(exe_ctx, strm,
76061da546Spatrick                                 (dump_mask & (~eDumpOptionType)) |
77061da546Spatrick                                     extra_dump_options);
78061da546Spatrick         break;
79061da546Spatrick       }
80061da546Spatrick     }
81061da546Spatrick     if (!one_line)
82061da546Spatrick       strm.IndentLess();
83061da546Spatrick   }
84061da546Spatrick }
85061da546Spatrick 
86*f6aab3d8Srobert llvm::json::Value
ToJSON(const ExecutionContext * exe_ctx)87*f6aab3d8Srobert OptionValueDictionary::ToJSON(const ExecutionContext *exe_ctx) {
88*f6aab3d8Srobert   llvm::json::Object dict;
89*f6aab3d8Srobert   for (const auto &value : m_values) {
90*f6aab3d8Srobert     dict.try_emplace(value.first.GetCString(), value.second->ToJSON(exe_ctx));
91*f6aab3d8Srobert   }
92*f6aab3d8Srobert   return dict;
93*f6aab3d8Srobert }
94*f6aab3d8Srobert 
GetArgs(Args & args) const95061da546Spatrick size_t OptionValueDictionary::GetArgs(Args &args) const {
96061da546Spatrick   args.Clear();
97061da546Spatrick   collection::const_iterator pos, end = m_values.end();
98061da546Spatrick   for (pos = m_values.begin(); pos != end; ++pos) {
99061da546Spatrick     StreamString strm;
100061da546Spatrick     strm.Printf("%s=", pos->first.GetCString());
101061da546Spatrick     pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
102061da546Spatrick     args.AppendArgument(strm.GetString());
103061da546Spatrick   }
104061da546Spatrick   return args.GetArgumentCount();
105061da546Spatrick }
106061da546Spatrick 
SetArgs(const Args & args,VarSetOperationType op)107061da546Spatrick Status OptionValueDictionary::SetArgs(const Args &args,
108061da546Spatrick                                       VarSetOperationType op) {
109061da546Spatrick   Status error;
110061da546Spatrick   const size_t argc = args.GetArgumentCount();
111061da546Spatrick   switch (op) {
112061da546Spatrick   case eVarSetOperationClear:
113061da546Spatrick     Clear();
114061da546Spatrick     break;
115061da546Spatrick 
116061da546Spatrick   case eVarSetOperationAppend:
117061da546Spatrick   case eVarSetOperationReplace:
118061da546Spatrick   case eVarSetOperationAssign:
119061da546Spatrick     if (argc == 0) {
120061da546Spatrick       error.SetErrorString(
121061da546Spatrick           "assign operation takes one or more key=value arguments");
122061da546Spatrick       return error;
123061da546Spatrick     }
124061da546Spatrick     for (const auto &entry : args) {
125061da546Spatrick       if (entry.ref().empty()) {
126061da546Spatrick         error.SetErrorString("empty argument");
127061da546Spatrick         return error;
128061da546Spatrick       }
129061da546Spatrick       if (!entry.ref().contains('=')) {
130061da546Spatrick         error.SetErrorString(
131061da546Spatrick             "assign operation takes one or more key=value arguments");
132061da546Spatrick         return error;
133061da546Spatrick       }
134061da546Spatrick 
135061da546Spatrick       llvm::StringRef key, value;
136061da546Spatrick       std::tie(key, value) = entry.ref().split('=');
137061da546Spatrick       bool key_valid = false;
138061da546Spatrick       if (key.empty()) {
139061da546Spatrick         error.SetErrorString("empty dictionary key");
140061da546Spatrick         return error;
141061da546Spatrick       }
142061da546Spatrick 
143061da546Spatrick       if (key.front() == '[') {
144061da546Spatrick         // Key name starts with '[', so the key value must be in single or
145061da546Spatrick         // double quotes like: ['<key>'] ["<key>"]
146061da546Spatrick         if ((key.size() > 2) && (key.back() == ']')) {
147061da546Spatrick           // Strip leading '[' and trailing ']'
148061da546Spatrick           key = key.substr(1, key.size() - 2);
149061da546Spatrick           const char quote_char = key.front();
150061da546Spatrick           if ((quote_char == '\'') || (quote_char == '"')) {
151061da546Spatrick             if ((key.size() > 2) && (key.back() == quote_char)) {
152061da546Spatrick               // Strip the quotes
153061da546Spatrick               key = key.substr(1, key.size() - 2);
154061da546Spatrick               key_valid = true;
155061da546Spatrick             }
156061da546Spatrick           } else {
157061da546Spatrick             // square brackets, no quotes
158061da546Spatrick             key_valid = true;
159061da546Spatrick           }
160061da546Spatrick         }
161061da546Spatrick       } else {
162061da546Spatrick         // No square brackets or quotes
163061da546Spatrick         key_valid = true;
164061da546Spatrick       }
165061da546Spatrick       if (!key_valid) {
166061da546Spatrick         error.SetErrorStringWithFormat(
167061da546Spatrick             "invalid key \"%s\", the key must be a bare string or "
168061da546Spatrick             "surrounded by brackets with optional quotes: [<key>] or "
169061da546Spatrick             "['<key>'] or [\"<key>\"]",
170061da546Spatrick             key.str().c_str());
171061da546Spatrick         return error;
172061da546Spatrick       }
173061da546Spatrick 
174*f6aab3d8Srobert       if (m_type_mask == 1u << eTypeEnum) {
175*f6aab3d8Srobert         auto enum_value =
176*f6aab3d8Srobert             std::make_shared<OptionValueEnumeration>(m_enum_values, 0);
177*f6aab3d8Srobert         error = enum_value->SetValueFromString(value);
178*f6aab3d8Srobert         if (error.Fail())
179*f6aab3d8Srobert           return error;
180*f6aab3d8Srobert         m_value_was_set = true;
181*f6aab3d8Srobert         SetValueForKey(ConstString(key), enum_value, true);
182*f6aab3d8Srobert       } else {
183061da546Spatrick         lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
184061da546Spatrick             value.str().c_str(), m_type_mask, error));
185061da546Spatrick         if (value_sp) {
186061da546Spatrick           if (error.Fail())
187061da546Spatrick             return error;
188061da546Spatrick           m_value_was_set = true;
189061da546Spatrick           SetValueForKey(ConstString(key), value_sp, true);
190061da546Spatrick         } else {
191061da546Spatrick           error.SetErrorString("dictionaries that can contain multiple types "
192061da546Spatrick                                "must subclass OptionValueArray");
193061da546Spatrick         }
194061da546Spatrick       }
195*f6aab3d8Srobert     }
196061da546Spatrick     break;
197061da546Spatrick 
198061da546Spatrick   case eVarSetOperationRemove:
199061da546Spatrick     if (argc > 0) {
200061da546Spatrick       for (size_t i = 0; i < argc; ++i) {
201061da546Spatrick         ConstString key(args.GetArgumentAtIndex(i));
202061da546Spatrick         if (!DeleteValueForKey(key)) {
203061da546Spatrick           error.SetErrorStringWithFormat(
204061da546Spatrick               "no value found named '%s', aborting remove operation",
205061da546Spatrick               key.GetCString());
206061da546Spatrick           break;
207061da546Spatrick         }
208061da546Spatrick       }
209061da546Spatrick     } else {
210061da546Spatrick       error.SetErrorString("remove operation takes one or more key arguments");
211061da546Spatrick     }
212061da546Spatrick     break;
213061da546Spatrick 
214061da546Spatrick   case eVarSetOperationInsertBefore:
215061da546Spatrick   case eVarSetOperationInsertAfter:
216061da546Spatrick   case eVarSetOperationInvalid:
217061da546Spatrick     error = OptionValue::SetValueFromString(llvm::StringRef(), op);
218061da546Spatrick     break;
219061da546Spatrick   }
220061da546Spatrick   return error;
221061da546Spatrick }
222061da546Spatrick 
SetValueFromString(llvm::StringRef value,VarSetOperationType op)223061da546Spatrick Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
224061da546Spatrick                                                  VarSetOperationType op) {
225061da546Spatrick   Args args(value.str());
226061da546Spatrick   Status error = SetArgs(args, op);
227061da546Spatrick   if (error.Success())
228061da546Spatrick     NotifyValueChanged();
229061da546Spatrick   return error;
230061da546Spatrick }
231061da546Spatrick 
232061da546Spatrick lldb::OptionValueSP
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,bool will_modify,Status & error) const233061da546Spatrick OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
234061da546Spatrick                                    llvm::StringRef name, bool will_modify,
235061da546Spatrick                                    Status &error) const {
236061da546Spatrick   lldb::OptionValueSP value_sp;
237061da546Spatrick   if (name.empty())
238061da546Spatrick     return nullptr;
239061da546Spatrick 
240061da546Spatrick   llvm::StringRef left, temp;
241061da546Spatrick   std::tie(left, temp) = name.split('[');
242061da546Spatrick   if (left.size() == name.size()) {
243061da546Spatrick     error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
244061da546Spatrick       "support '[<key>]' subvalues where <key> "
245061da546Spatrick       "a string value optionally delimited by "
246061da546Spatrick       "single or double quotes",
247061da546Spatrick       name.str().c_str(), GetTypeAsCString());
248061da546Spatrick     return nullptr;
249061da546Spatrick   }
250061da546Spatrick   assert(!temp.empty());
251061da546Spatrick 
252061da546Spatrick   llvm::StringRef key, quote_char;
253061da546Spatrick 
254061da546Spatrick   if (temp[0] == '\"' || temp[0] == '\'') {
255061da546Spatrick     quote_char = temp.take_front();
256061da546Spatrick     temp = temp.drop_front();
257061da546Spatrick   }
258061da546Spatrick 
259061da546Spatrick   llvm::StringRef sub_name;
260061da546Spatrick   std::tie(key, sub_name) = temp.split(']');
261061da546Spatrick 
262061da546Spatrick   if (!key.consume_back(quote_char) || key.empty()) {
263061da546Spatrick     error.SetErrorStringWithFormat("invalid value path '%s', "
264061da546Spatrick       "key names must be formatted as ['<key>'] where <key> "
265061da546Spatrick       "is a string that doesn't contain quotes and the quote"
266061da546Spatrick       " char is optional", name.str().c_str());
267061da546Spatrick     return nullptr;
268061da546Spatrick   }
269061da546Spatrick 
270061da546Spatrick   value_sp = GetValueForKey(ConstString(key));
271061da546Spatrick   if (!value_sp) {
272061da546Spatrick     error.SetErrorStringWithFormat(
273061da546Spatrick       "dictionary does not contain a value for the key name '%s'",
274061da546Spatrick       key.str().c_str());
275061da546Spatrick     return nullptr;
276061da546Spatrick   }
277061da546Spatrick 
278061da546Spatrick   if (sub_name.empty())
279061da546Spatrick     return value_sp;
280061da546Spatrick   return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
281061da546Spatrick }
282061da546Spatrick 
SetSubValue(const ExecutionContext * exe_ctx,VarSetOperationType op,llvm::StringRef name,llvm::StringRef value)283061da546Spatrick Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
284061da546Spatrick                                           VarSetOperationType op,
285061da546Spatrick                                           llvm::StringRef name,
286061da546Spatrick                                           llvm::StringRef value) {
287061da546Spatrick   Status error;
288061da546Spatrick   const bool will_modify = true;
289061da546Spatrick   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
290061da546Spatrick   if (value_sp)
291061da546Spatrick     error = value_sp->SetValueFromString(value, op);
292061da546Spatrick   else {
293061da546Spatrick     if (error.AsCString() == nullptr)
294061da546Spatrick       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
295061da546Spatrick   }
296061da546Spatrick   return error;
297061da546Spatrick }
298061da546Spatrick 
299061da546Spatrick lldb::OptionValueSP
GetValueForKey(ConstString key) const300061da546Spatrick OptionValueDictionary::GetValueForKey(ConstString key) const {
301061da546Spatrick   lldb::OptionValueSP value_sp;
302061da546Spatrick   collection::const_iterator pos = m_values.find(key);
303061da546Spatrick   if (pos != m_values.end())
304061da546Spatrick     value_sp = pos->second;
305061da546Spatrick   return value_sp;
306061da546Spatrick }
307061da546Spatrick 
SetValueForKey(ConstString key,const lldb::OptionValueSP & value_sp,bool can_replace)308061da546Spatrick bool OptionValueDictionary::SetValueForKey(ConstString key,
309061da546Spatrick                                            const lldb::OptionValueSP &value_sp,
310061da546Spatrick                                            bool can_replace) {
311061da546Spatrick   // Make sure the value_sp object is allowed to contain values of the type
312061da546Spatrick   // passed in...
313061da546Spatrick   if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
314061da546Spatrick     if (!can_replace) {
315061da546Spatrick       collection::const_iterator pos = m_values.find(key);
316061da546Spatrick       if (pos != m_values.end())
317061da546Spatrick         return false;
318061da546Spatrick     }
319061da546Spatrick     m_values[key] = value_sp;
320061da546Spatrick     return true;
321061da546Spatrick   }
322061da546Spatrick   return false;
323061da546Spatrick }
324061da546Spatrick 
DeleteValueForKey(ConstString key)325061da546Spatrick bool OptionValueDictionary::DeleteValueForKey(ConstString key) {
326061da546Spatrick   collection::iterator pos = m_values.find(key);
327061da546Spatrick   if (pos != m_values.end()) {
328061da546Spatrick     m_values.erase(pos);
329061da546Spatrick     return true;
330061da546Spatrick   }
331061da546Spatrick   return false;
332061da546Spatrick }
333061da546Spatrick 
334be691f3bSpatrick OptionValueSP
DeepCopy(const OptionValueSP & new_parent) const335be691f3bSpatrick OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const {
336be691f3bSpatrick   auto copy_sp = OptionValue::DeepCopy(new_parent);
337be691f3bSpatrick   // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived
338be691f3bSpatrick   // types that override GetType returning a different value.
339be691f3bSpatrick   auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get());
340be691f3bSpatrick   lldbassert(dict_value_ptr);
341be691f3bSpatrick 
342be691f3bSpatrick   for (auto &value : dict_value_ptr->m_values)
343be691f3bSpatrick     value.second = value.second->DeepCopy(copy_sp);
344be691f3bSpatrick 
345be691f3bSpatrick   return copy_sp;
346061da546Spatrick }
347