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