OptionValueDictionary.cpp revision 360784
1//===-- OptionValueDictionary.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/OptionValueDictionary.h"
10
11#include "llvm/ADT/StringRef.h"
12#include "lldb/DataFormatters/FormatManager.h"
13#include "lldb/Interpreter/OptionValueString.h"
14#include "lldb/Utility/Args.h"
15#include "lldb/Utility/State.h"
16
17using namespace lldb;
18using namespace lldb_private;
19
20void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
21                                      Stream &strm, uint32_t dump_mask) {
22  const Type dict_type = ConvertTypeMaskToType(m_type_mask);
23  if (dump_mask & eDumpOptionType) {
24    if (m_type_mask != eTypeInvalid)
25      strm.Printf("(%s of %ss)", GetTypeAsCString(),
26                  GetBuiltinTypeAsCString(dict_type));
27    else
28      strm.Printf("(%s)", GetTypeAsCString());
29  }
30  if (dump_mask & eDumpOptionValue) {
31    const bool one_line = dump_mask & eDumpOptionCommand;
32    if (dump_mask & eDumpOptionType)
33      strm.PutCString(" =");
34
35    collection::iterator pos, end = m_values.end();
36
37    if (!one_line)
38      strm.IndentMore();
39
40    for (pos = m_values.begin(); pos != end; ++pos) {
41      OptionValue *option_value = pos->second.get();
42
43      if (one_line)
44        strm << ' ';
45      else
46        strm.EOL();
47
48      strm.Indent(pos->first.GetCString());
49
50      const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
51      switch (dict_type) {
52      default:
53      case eTypeArray:
54      case eTypeDictionary:
55      case eTypeProperties:
56      case eTypeFileSpecList:
57      case eTypePathMap:
58        strm.PutChar(' ');
59        option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
60        break;
61
62      case eTypeBoolean:
63      case eTypeChar:
64      case eTypeEnum:
65      case eTypeFileSpec:
66      case eTypeFormat:
67      case eTypeSInt64:
68      case eTypeString:
69      case eTypeUInt64:
70      case eTypeUUID:
71        // No need to show the type for dictionaries of simple items
72        strm.PutCString("=");
73        option_value->DumpValue(exe_ctx, strm,
74                                (dump_mask & (~eDumpOptionType)) |
75                                    extra_dump_options);
76        break;
77      }
78    }
79    if (!one_line)
80      strm.IndentLess();
81  }
82}
83
84size_t OptionValueDictionary::GetArgs(Args &args) const {
85  args.Clear();
86  collection::const_iterator pos, end = m_values.end();
87  for (pos = m_values.begin(); pos != end; ++pos) {
88    StreamString strm;
89    strm.Printf("%s=", pos->first.GetCString());
90    pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
91    args.AppendArgument(strm.GetString());
92  }
93  return args.GetArgumentCount();
94}
95
96Status OptionValueDictionary::SetArgs(const Args &args,
97                                      VarSetOperationType op) {
98  Status error;
99  const size_t argc = args.GetArgumentCount();
100  switch (op) {
101  case eVarSetOperationClear:
102    Clear();
103    break;
104
105  case eVarSetOperationAppend:
106  case eVarSetOperationReplace:
107  case eVarSetOperationAssign:
108    if (argc == 0) {
109      error.SetErrorString(
110          "assign operation takes one or more key=value arguments");
111      return error;
112    }
113    for (const auto &entry : args) {
114      if (entry.ref().empty()) {
115        error.SetErrorString("empty argument");
116        return error;
117      }
118      if (!entry.ref().contains('=')) {
119        error.SetErrorString(
120            "assign operation takes one or more key=value arguments");
121        return error;
122      }
123
124      llvm::StringRef key, value;
125      std::tie(key, value) = entry.ref().split('=');
126      bool key_valid = false;
127      if (key.empty()) {
128        error.SetErrorString("empty dictionary key");
129        return error;
130      }
131
132      if (key.front() == '[') {
133        // Key name starts with '[', so the key value must be in single or
134        // double quotes like: ['<key>'] ["<key>"]
135        if ((key.size() > 2) && (key.back() == ']')) {
136          // Strip leading '[' and trailing ']'
137          key = key.substr(1, key.size() - 2);
138          const char quote_char = key.front();
139          if ((quote_char == '\'') || (quote_char == '"')) {
140            if ((key.size() > 2) && (key.back() == quote_char)) {
141              // Strip the quotes
142              key = key.substr(1, key.size() - 2);
143              key_valid = true;
144            }
145          } else {
146            // square brackets, no quotes
147            key_valid = true;
148          }
149        }
150      } else {
151        // No square brackets or quotes
152        key_valid = true;
153      }
154      if (!key_valid) {
155        error.SetErrorStringWithFormat(
156            "invalid key \"%s\", the key must be a bare string or "
157            "surrounded by brackets with optional quotes: [<key>] or "
158            "['<key>'] or [\"<key>\"]",
159            key.str().c_str());
160        return error;
161      }
162
163      lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
164          value.str().c_str(), m_type_mask, error));
165      if (value_sp) {
166        if (error.Fail())
167          return error;
168        m_value_was_set = true;
169        SetValueForKey(ConstString(key), value_sp, true);
170      } else {
171        error.SetErrorString("dictionaries that can contain multiple types "
172                             "must subclass OptionValueArray");
173      }
174    }
175    break;
176
177  case eVarSetOperationRemove:
178    if (argc > 0) {
179      for (size_t i = 0; i < argc; ++i) {
180        ConstString key(args.GetArgumentAtIndex(i));
181        if (!DeleteValueForKey(key)) {
182          error.SetErrorStringWithFormat(
183              "no value found named '%s', aborting remove operation",
184              key.GetCString());
185          break;
186        }
187      }
188    } else {
189      error.SetErrorString("remove operation takes one or more key arguments");
190    }
191    break;
192
193  case eVarSetOperationInsertBefore:
194  case eVarSetOperationInsertAfter:
195  case eVarSetOperationInvalid:
196    error = OptionValue::SetValueFromString(llvm::StringRef(), op);
197    break;
198  }
199  return error;
200}
201
202Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
203                                                 VarSetOperationType op) {
204  Args args(value.str());
205  Status error = SetArgs(args, op);
206  if (error.Success())
207    NotifyValueChanged();
208  return error;
209}
210
211lldb::OptionValueSP
212OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
213                                   llvm::StringRef name, bool will_modify,
214                                   Status &error) const {
215  lldb::OptionValueSP value_sp;
216  if (name.empty())
217    return nullptr;
218
219  llvm::StringRef left, temp;
220  std::tie(left, temp) = name.split('[');
221  if (left.size() == name.size()) {
222    error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
223      "support '[<key>]' subvalues where <key> "
224      "a string value optionally delimited by "
225      "single or double quotes",
226      name.str().c_str(), GetTypeAsCString());
227    return nullptr;
228  }
229  assert(!temp.empty());
230
231  llvm::StringRef key, quote_char;
232
233  if (temp[0] == '\"' || temp[0] == '\'') {
234    quote_char = temp.take_front();
235    temp = temp.drop_front();
236  }
237
238  llvm::StringRef sub_name;
239  std::tie(key, sub_name) = temp.split(']');
240
241  if (!key.consume_back(quote_char) || key.empty()) {
242    error.SetErrorStringWithFormat("invalid value path '%s', "
243      "key names must be formatted as ['<key>'] where <key> "
244      "is a string that doesn't contain quotes and the quote"
245      " char is optional", name.str().c_str());
246    return nullptr;
247  }
248
249  value_sp = GetValueForKey(ConstString(key));
250  if (!value_sp) {
251    error.SetErrorStringWithFormat(
252      "dictionary does not contain a value for the key name '%s'",
253      key.str().c_str());
254    return nullptr;
255  }
256
257  if (sub_name.empty())
258    return value_sp;
259  return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
260}
261
262Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
263                                          VarSetOperationType op,
264                                          llvm::StringRef name,
265                                          llvm::StringRef value) {
266  Status error;
267  const bool will_modify = true;
268  lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
269  if (value_sp)
270    error = value_sp->SetValueFromString(value, op);
271  else {
272    if (error.AsCString() == nullptr)
273      error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
274  }
275  return error;
276}
277
278lldb::OptionValueSP
279OptionValueDictionary::GetValueForKey(ConstString key) const {
280  lldb::OptionValueSP value_sp;
281  collection::const_iterator pos = m_values.find(key);
282  if (pos != m_values.end())
283    value_sp = pos->second;
284  return value_sp;
285}
286
287bool OptionValueDictionary::SetValueForKey(ConstString key,
288                                           const lldb::OptionValueSP &value_sp,
289                                           bool can_replace) {
290  // Make sure the value_sp object is allowed to contain values of the type
291  // passed in...
292  if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
293    if (!can_replace) {
294      collection::const_iterator pos = m_values.find(key);
295      if (pos != m_values.end())
296        return false;
297    }
298    m_values[key] = value_sp;
299    return true;
300  }
301  return false;
302}
303
304bool OptionValueDictionary::DeleteValueForKey(ConstString key) {
305  collection::iterator pos = m_values.find(key);
306  if (pos != m_values.end()) {
307    m_values.erase(pos);
308    return true;
309  }
310  return false;
311}
312
313lldb::OptionValueSP OptionValueDictionary::DeepCopy() const {
314  OptionValueDictionary *copied_dict =
315      new OptionValueDictionary(m_type_mask, m_raw_value_dump);
316  lldb::OptionValueSP copied_value_sp(copied_dict);
317  collection::const_iterator pos, end = m_values.end();
318  for (pos = m_values.begin(); pos != end; ++pos) {
319    StreamString strm;
320    strm.Printf("%s=", pos->first.GetCString());
321    copied_dict->SetValueForKey(pos->first, pos->second->DeepCopy(), true);
322  }
323  return copied_value_sp;
324}
325