180814287SRaphael Isemann //===-- OptionValueDictionary.cpp -----------------------------------------===//
267cc0636SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667cc0636SGreg Clayton //
767cc0636SGreg Clayton //===----------------------------------------------------------------------===//
867cc0636SGreg Clayton 
967cc0636SGreg Clayton #include "lldb/Interpreter/OptionValueDictionary.h"
1067cc0636SGreg Clayton 
1167cc0636SGreg Clayton #include "llvm/ADT/StringRef.h"
125548cb50SEnrico Granata #include "lldb/DataFormatters/FormatManager.h"
1367cc0636SGreg Clayton #include "lldb/Interpreter/OptionValueString.h"
14145d95c9SPavel Labath #include "lldb/Utility/Args.h"
15d821c997SPavel Labath #include "lldb/Utility/State.h"
1667cc0636SGreg Clayton 
1767cc0636SGreg Clayton using namespace lldb;
1867cc0636SGreg Clayton using namespace lldb_private;
1967cc0636SGreg Clayton 
20b9c1b51eSKate Stone void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
21b9c1b51eSKate Stone                                       Stream &strm, uint32_t dump_mask) {
2267cc0636SGreg Clayton   const Type dict_type = ConvertTypeMaskToType(m_type_mask);
23b9c1b51eSKate Stone   if (dump_mask & eDumpOptionType) {
2467cc0636SGreg Clayton     if (m_type_mask != eTypeInvalid)
25b9c1b51eSKate Stone       strm.Printf("(%s of %ss)", GetTypeAsCString(),
26b9c1b51eSKate Stone                   GetBuiltinTypeAsCString(dict_type));
2767cc0636SGreg Clayton     else
2867cc0636SGreg Clayton       strm.Printf("(%s)", GetTypeAsCString());
2967cc0636SGreg Clayton   }
30b9c1b51eSKate Stone   if (dump_mask & eDumpOptionValue) {
31b76e25a2SJonas Devlieghere     const bool one_line = dump_mask & eDumpOptionCommand;
3267cc0636SGreg Clayton     if (dump_mask & eDumpOptionType)
3367cc0636SGreg Clayton       strm.PutCString(" =");
3467cc0636SGreg Clayton 
3567cc0636SGreg Clayton     collection::iterator pos, end = m_values.end();
3667cc0636SGreg Clayton 
37b76e25a2SJonas Devlieghere     if (!one_line)
3867cc0636SGreg Clayton       strm.IndentMore();
3967cc0636SGreg Clayton 
40b9c1b51eSKate Stone     for (pos = m_values.begin(); pos != end; ++pos) {
4167cc0636SGreg Clayton       OptionValue *option_value = pos->second.get();
42b76e25a2SJonas Devlieghere 
43b76e25a2SJonas Devlieghere       if (one_line)
44b76e25a2SJonas Devlieghere         strm << ' ';
45b76e25a2SJonas Devlieghere       else
4667cc0636SGreg Clayton         strm.EOL();
47b76e25a2SJonas Devlieghere 
48*9dfd4e26SRaphael Isemann       strm.Indent(pos->first.GetStringRef());
4967cc0636SGreg Clayton 
5067cc0636SGreg Clayton       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
51b9c1b51eSKate Stone       switch (dict_type) {
5267cc0636SGreg Clayton       default:
5367cc0636SGreg Clayton       case eTypeArray:
5467cc0636SGreg Clayton       case eTypeDictionary:
5567cc0636SGreg Clayton       case eTypeProperties:
5667cc0636SGreg Clayton       case eTypeFileSpecList:
5767cc0636SGreg Clayton       case eTypePathMap:
5867cc0636SGreg Clayton         strm.PutChar(' ');
5967cc0636SGreg Clayton         option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
6067cc0636SGreg Clayton         break;
6167cc0636SGreg Clayton 
6267cc0636SGreg Clayton       case eTypeBoolean:
633e7442b6SZachary Turner       case eTypeChar:
6467cc0636SGreg Clayton       case eTypeEnum:
6567cc0636SGreg Clayton       case eTypeFileSpec:
6667cc0636SGreg Clayton       case eTypeFormat:
6767cc0636SGreg Clayton       case eTypeSInt64:
6867cc0636SGreg Clayton       case eTypeString:
6967cc0636SGreg Clayton       case eTypeUInt64:
7067cc0636SGreg Clayton       case eTypeUUID:
7167cc0636SGreg Clayton         // No need to show the type for dictionaries of simple items
7267cc0636SGreg Clayton         strm.PutCString("=");
73b9c1b51eSKate Stone         option_value->DumpValue(exe_ctx, strm,
74b9c1b51eSKate Stone                                 (dump_mask & (~eDumpOptionType)) |
75b9c1b51eSKate Stone                                     extra_dump_options);
7667cc0636SGreg Clayton         break;
7767cc0636SGreg Clayton       }
7867cc0636SGreg Clayton     }
79b76e25a2SJonas Devlieghere     if (!one_line)
8067cc0636SGreg Clayton       strm.IndentLess();
8167cc0636SGreg Clayton   }
8267cc0636SGreg Clayton }
8367cc0636SGreg Clayton 
84b9c1b51eSKate Stone size_t OptionValueDictionary::GetArgs(Args &args) const {
8567cc0636SGreg Clayton   args.Clear();
8667cc0636SGreg Clayton   collection::const_iterator pos, end = m_values.end();
87b9c1b51eSKate Stone   for (pos = m_values.begin(); pos != end; ++pos) {
8867cc0636SGreg Clayton     StreamString strm;
8967cc0636SGreg Clayton     strm.Printf("%s=", pos->first.GetCString());
90d78c9576SEd Maste     pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
91ecbb0bb1SZachary Turner     args.AppendArgument(strm.GetString());
9267cc0636SGreg Clayton   }
9367cc0636SGreg Clayton   return args.GetArgumentCount();
9467cc0636SGreg Clayton }
9567cc0636SGreg Clayton 
9697206d57SZachary Turner Status OptionValueDictionary::SetArgs(const Args &args,
9797206d57SZachary Turner                                       VarSetOperationType op) {
9897206d57SZachary Turner   Status error;
9967cc0636SGreg Clayton   const size_t argc = args.GetArgumentCount();
100b9c1b51eSKate Stone   switch (op) {
10167cc0636SGreg Clayton   case eVarSetOperationClear:
10267cc0636SGreg Clayton     Clear();
10367cc0636SGreg Clayton     break;
10467cc0636SGreg Clayton 
10567cc0636SGreg Clayton   case eVarSetOperationAppend:
10667cc0636SGreg Clayton   case eVarSetOperationReplace:
10767cc0636SGreg Clayton   case eVarSetOperationAssign:
108d6a24757SZachary Turner     if (argc == 0) {
109d6a24757SZachary Turner       error.SetErrorString(
110d6a24757SZachary Turner           "assign operation takes one or more key=value arguments");
111d6a24757SZachary Turner       return error;
112d6a24757SZachary Turner     }
113d6a24757SZachary Turner     for (const auto &entry : args) {
1140d9a201eSRaphael Isemann       if (entry.ref().empty()) {
115d6a24757SZachary Turner         error.SetErrorString("empty argument");
116d6a24757SZachary Turner         return error;
117d6a24757SZachary Turner       }
1180d9a201eSRaphael Isemann       if (!entry.ref().contains('=')) {
119b9c1b51eSKate Stone         error.SetErrorString(
120b9c1b51eSKate Stone             "assign operation takes one or more key=value arguments");
121a44c0e31SEnrico Granata         return error;
122a44c0e31SEnrico Granata       }
123a44c0e31SEnrico Granata 
124d6a24757SZachary Turner       llvm::StringRef key, value;
1250d9a201eSRaphael Isemann       std::tie(key, value) = entry.ref().split('=');
12667cc0636SGreg Clayton       bool key_valid = false;
127d6a24757SZachary Turner       if (key.empty()) {
128d6a24757SZachary Turner         error.SetErrorString("empty dictionary key");
129d6a24757SZachary Turner         return error;
130d6a24757SZachary Turner       }
131d6a24757SZachary Turner 
132b9c1b51eSKate Stone       if (key.front() == '[') {
133b9c1b51eSKate Stone         // Key name starts with '[', so the key value must be in single or
13405097246SAdrian Prantl         // double quotes like: ['<key>'] ["<key>"]
135b9c1b51eSKate Stone         if ((key.size() > 2) && (key.back() == ']')) {
13667cc0636SGreg Clayton           // Strip leading '[' and trailing ']'
13767cc0636SGreg Clayton           key = key.substr(1, key.size() - 2);
13867cc0636SGreg Clayton           const char quote_char = key.front();
139b9c1b51eSKate Stone           if ((quote_char == '\'') || (quote_char == '"')) {
140b9c1b51eSKate Stone             if ((key.size() > 2) && (key.back() == quote_char)) {
14167cc0636SGreg Clayton               // Strip the quotes
14267cc0636SGreg Clayton               key = key.substr(1, key.size() - 2);
14367cc0636SGreg Clayton               key_valid = true;
14467cc0636SGreg Clayton             }
145b9c1b51eSKate Stone           } else {
14667cc0636SGreg Clayton             // square brackets, no quotes
14767cc0636SGreg Clayton             key_valid = true;
14867cc0636SGreg Clayton           }
14967cc0636SGreg Clayton         }
150b9c1b51eSKate Stone       } else {
15167cc0636SGreg Clayton         // No square brackets or quotes
15267cc0636SGreg Clayton         key_valid = true;
15367cc0636SGreg Clayton       }
154b9c1b51eSKate Stone       if (!key_valid) {
155b9c1b51eSKate Stone         error.SetErrorStringWithFormat(
156b9c1b51eSKate Stone             "invalid key \"%s\", the key must be a bare string or "
157b9c1b51eSKate Stone             "surrounded by brackets with optional quotes: [<key>] or "
158b9c1b51eSKate Stone             "['<key>'] or [\"<key>\"]",
159d6a24757SZachary Turner             key.str().c_str());
16067cc0636SGreg Clayton         return error;
16167cc0636SGreg Clayton       }
16267cc0636SGreg Clayton 
163b9c1b51eSKate Stone       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
164d6a24757SZachary Turner           value.str().c_str(), m_type_mask, error));
165b9c1b51eSKate Stone       if (value_sp) {
16667cc0636SGreg Clayton         if (error.Fail())
16767cc0636SGreg Clayton           return error;
16867cc0636SGreg Clayton         m_value_was_set = true;
16967cc0636SGreg Clayton         SetValueForKey(ConstString(key), value_sp, true);
170b9c1b51eSKate Stone       } else {
171b9c1b51eSKate Stone         error.SetErrorString("dictionaries that can contain multiple types "
172b9c1b51eSKate Stone                              "must subclass OptionValueArray");
17367cc0636SGreg Clayton       }
17467cc0636SGreg Clayton     }
17567cc0636SGreg Clayton     break;
17667cc0636SGreg Clayton 
17767cc0636SGreg Clayton   case eVarSetOperationRemove:
178b9c1b51eSKate Stone     if (argc > 0) {
179b9c1b51eSKate Stone       for (size_t i = 0; i < argc; ++i) {
18067cc0636SGreg Clayton         ConstString key(args.GetArgumentAtIndex(i));
181b9c1b51eSKate Stone         if (!DeleteValueForKey(key)) {
182b9c1b51eSKate Stone           error.SetErrorStringWithFormat(
183b9c1b51eSKate Stone               "no value found named '%s', aborting remove operation",
184b9c1b51eSKate Stone               key.GetCString());
18567cc0636SGreg Clayton           break;
18667cc0636SGreg Clayton         }
18767cc0636SGreg Clayton       }
188b9c1b51eSKate Stone     } else {
18967cc0636SGreg Clayton       error.SetErrorString("remove operation takes one or more key arguments");
19067cc0636SGreg Clayton     }
19167cc0636SGreg Clayton     break;
19267cc0636SGreg Clayton 
19367cc0636SGreg Clayton   case eVarSetOperationInsertBefore:
19467cc0636SGreg Clayton   case eVarSetOperationInsertAfter:
19567cc0636SGreg Clayton   case eVarSetOperationInvalid:
196c95f7e2aSPavel Labath     error = OptionValue::SetValueFromString(llvm::StringRef(), op);
19767cc0636SGreg Clayton     break;
19867cc0636SGreg Clayton   }
19967cc0636SGreg Clayton   return error;
20067cc0636SGreg Clayton }
20167cc0636SGreg Clayton 
20297206d57SZachary Turner Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
203b9c1b51eSKate Stone                                                  VarSetOperationType op) {
204771ef6d4SMalcolm Parsons   Args args(value.str());
20597206d57SZachary Turner   Status error = SetArgs(args, op);
206332e8b1cSGreg Clayton   if (error.Success())
207332e8b1cSGreg Clayton     NotifyValueChanged();
208332e8b1cSGreg Clayton   return error;
20967cc0636SGreg Clayton }
21067cc0636SGreg Clayton 
21167cc0636SGreg Clayton lldb::OptionValueSP
212b9c1b51eSKate Stone OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
21331d97a5cSZachary Turner                                    llvm::StringRef name, bool will_modify,
21497206d57SZachary Turner                                    Status &error) const {
21567cc0636SGreg Clayton   lldb::OptionValueSP value_sp;
21631d97a5cSZachary Turner   if (name.empty())
21731d97a5cSZachary Turner     return nullptr;
21867cc0636SGreg Clayton 
21931d97a5cSZachary Turner   llvm::StringRef left, temp;
22031d97a5cSZachary Turner   std::tie(left, temp) = name.split('[');
22131d97a5cSZachary Turner   if (left.size() == name.size()) {
222b9c1b51eSKate Stone     error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
223b9c1b51eSKate Stone       "support '[<key>]' subvalues where <key> "
224b9c1b51eSKate Stone       "a string value optionally delimited by "
225b9c1b51eSKate Stone       "single or double quotes",
22631d97a5cSZachary Turner       name.str().c_str(), GetTypeAsCString());
22731d97a5cSZachary Turner     return nullptr;
22867cc0636SGreg Clayton   }
22931d97a5cSZachary Turner   assert(!temp.empty());
23031d97a5cSZachary Turner 
2318e353b90SDavide Italiano   llvm::StringRef key, quote_char;
23231d97a5cSZachary Turner 
23331d97a5cSZachary Turner   if (temp[0] == '\"' || temp[0] == '\'') {
23431d97a5cSZachary Turner     quote_char = temp.take_front();
23531d97a5cSZachary Turner     temp = temp.drop_front();
23667cc0636SGreg Clayton   }
23731d97a5cSZachary Turner 
23831d97a5cSZachary Turner   llvm::StringRef sub_name;
23931d97a5cSZachary Turner   std::tie(key, sub_name) = temp.split(']');
24031d97a5cSZachary Turner 
24131d97a5cSZachary Turner   if (!key.consume_back(quote_char) || key.empty()) {
24231d97a5cSZachary Turner     error.SetErrorStringWithFormat("invalid value path '%s', "
24331d97a5cSZachary Turner       "key names must be formatted as ['<key>'] where <key> "
24431d97a5cSZachary Turner       "is a string that doesn't contain quotes and the quote"
24531d97a5cSZachary Turner       " char is optional", name.str().c_str());
24631d97a5cSZachary Turner     return nullptr;
24731d97a5cSZachary Turner   }
24831d97a5cSZachary Turner 
24931d97a5cSZachary Turner   value_sp = GetValueForKey(ConstString(key));
25031d97a5cSZachary Turner   if (!value_sp) {
25131d97a5cSZachary Turner     error.SetErrorStringWithFormat(
25231d97a5cSZachary Turner       "dictionary does not contain a value for the key name '%s'",
25331d97a5cSZachary Turner       key.str().c_str());
25431d97a5cSZachary Turner     return nullptr;
25531d97a5cSZachary Turner   }
25631d97a5cSZachary Turner 
25731d97a5cSZachary Turner   if (sub_name.empty())
25867cc0636SGreg Clayton     return value_sp;
25931d97a5cSZachary Turner   return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
26067cc0636SGreg Clayton }
26167cc0636SGreg Clayton 
26297206d57SZachary Turner Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
263b9c1b51eSKate Stone                                           VarSetOperationType op,
26497206d57SZachary Turner                                           llvm::StringRef name,
26597206d57SZachary Turner                                           llvm::StringRef value) {
26697206d57SZachary Turner   Status error;
26767cc0636SGreg Clayton   const bool will_modify = true;
26867cc0636SGreg Clayton   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
26967cc0636SGreg Clayton   if (value_sp)
27031d97a5cSZachary Turner     error = value_sp->SetValueFromString(value, op);
271b9c1b51eSKate Stone   else {
272d78c9576SEd Maste     if (error.AsCString() == nullptr)
27331d97a5cSZachary Turner       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
27467cc0636SGreg Clayton   }
27567cc0636SGreg Clayton   return error;
27667cc0636SGreg Clayton }
27767cc0636SGreg Clayton 
27867cc0636SGreg Clayton lldb::OptionValueSP
2790e4c4821SAdrian Prantl OptionValueDictionary::GetValueForKey(ConstString key) const {
28067cc0636SGreg Clayton   lldb::OptionValueSP value_sp;
28167cc0636SGreg Clayton   collection::const_iterator pos = m_values.find(key);
28267cc0636SGreg Clayton   if (pos != m_values.end())
28367cc0636SGreg Clayton     value_sp = pos->second;
28467cc0636SGreg Clayton   return value_sp;
28567cc0636SGreg Clayton }
28667cc0636SGreg Clayton 
2870e4c4821SAdrian Prantl bool OptionValueDictionary::SetValueForKey(ConstString key,
28867cc0636SGreg Clayton                                            const lldb::OptionValueSP &value_sp,
289b9c1b51eSKate Stone                                            bool can_replace) {
29005097246SAdrian Prantl   // Make sure the value_sp object is allowed to contain values of the type
29105097246SAdrian Prantl   // passed in...
292b9c1b51eSKate Stone   if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
293b9c1b51eSKate Stone     if (!can_replace) {
29467cc0636SGreg Clayton       collection::const_iterator pos = m_values.find(key);
29567cc0636SGreg Clayton       if (pos != m_values.end())
29667cc0636SGreg Clayton         return false;
29767cc0636SGreg Clayton     }
29867cc0636SGreg Clayton     m_values[key] = value_sp;
29967cc0636SGreg Clayton     return true;
30067cc0636SGreg Clayton   }
30167cc0636SGreg Clayton   return false;
30267cc0636SGreg Clayton }
30367cc0636SGreg Clayton 
3040e4c4821SAdrian Prantl bool OptionValueDictionary::DeleteValueForKey(ConstString key) {
30567cc0636SGreg Clayton   collection::iterator pos = m_values.find(key);
306b9c1b51eSKate Stone   if (pos != m_values.end()) {
30767cc0636SGreg Clayton     m_values.erase(pos);
30867cc0636SGreg Clayton     return true;
30967cc0636SGreg Clayton   }
31067cc0636SGreg Clayton   return false;
31167cc0636SGreg Clayton }
31267cc0636SGreg Clayton 
313b9c1b51eSKate Stone lldb::OptionValueSP OptionValueDictionary::DeepCopy() const {
314b9c1b51eSKate Stone   OptionValueDictionary *copied_dict =
315b9c1b51eSKate Stone       new OptionValueDictionary(m_type_mask, m_raw_value_dump);
31667cc0636SGreg Clayton   lldb::OptionValueSP copied_value_sp(copied_dict);
31767cc0636SGreg Clayton   collection::const_iterator pos, end = m_values.end();
318b9c1b51eSKate Stone   for (pos = m_values.begin(); pos != end; ++pos) {
31967cc0636SGreg Clayton     StreamString strm;
32067cc0636SGreg Clayton     strm.Printf("%s=", pos->first.GetCString());
32167cc0636SGreg Clayton     copied_dict->SetValueForKey(pos->first, pos->second->DeepCopy(), true);
32267cc0636SGreg Clayton   }
32367cc0636SGreg Clayton   return copied_value_sp;
32467cc0636SGreg Clayton }
325