15ffd83dbSDimitry Andric //===-- OptionValueDictionary.cpp -----------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Interpreter/OptionValueDictionary.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "lldb/DataFormatters/FormatManager.h"
1281ad6265SDimitry Andric #include "lldb/Interpreter/OptionValueEnumeration.h"
130b57cec5SDimitry Andric #include "lldb/Interpreter/OptionValueString.h"
140b57cec5SDimitry Andric #include "lldb/Utility/Args.h"
150b57cec5SDimitry Andric #include "lldb/Utility/State.h"
1681ad6265SDimitry Andric #include "llvm/ADT/StringRef.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric using namespace lldb;
190b57cec5SDimitry Andric using namespace lldb_private;
200b57cec5SDimitry Andric 
DumpValue(const ExecutionContext * exe_ctx,Stream & strm,uint32_t dump_mask)210b57cec5SDimitry Andric void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
220b57cec5SDimitry Andric                                       Stream &strm, uint32_t dump_mask) {
230b57cec5SDimitry Andric   const Type dict_type = ConvertTypeMaskToType(m_type_mask);
240b57cec5SDimitry Andric   if (dump_mask & eDumpOptionType) {
250b57cec5SDimitry Andric     if (m_type_mask != eTypeInvalid)
260b57cec5SDimitry Andric       strm.Printf("(%s of %ss)", GetTypeAsCString(),
270b57cec5SDimitry Andric                   GetBuiltinTypeAsCString(dict_type));
280b57cec5SDimitry Andric     else
290b57cec5SDimitry Andric       strm.Printf("(%s)", GetTypeAsCString());
300b57cec5SDimitry Andric   }
310b57cec5SDimitry Andric   if (dump_mask & eDumpOptionValue) {
320b57cec5SDimitry Andric     const bool one_line = dump_mask & eDumpOptionCommand;
330b57cec5SDimitry Andric     if (dump_mask & eDumpOptionType)
340b57cec5SDimitry Andric       strm.PutCString(" =");
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric     if (!one_line)
370b57cec5SDimitry Andric       strm.IndentMore();
380b57cec5SDimitry Andric 
3906c3fb27SDimitry Andric     // m_values is not guaranteed to be sorted alphabetically, so for
4006c3fb27SDimitry Andric     // consistentcy we will sort them here before dumping
4106c3fb27SDimitry Andric     std::map<llvm::StringRef, OptionValue *> sorted_values;
4206c3fb27SDimitry Andric     for (const auto &value : m_values) {
4306c3fb27SDimitry Andric       sorted_values[value.first()] = value.second.get();
4406c3fb27SDimitry Andric     }
4506c3fb27SDimitry Andric     for (const auto &value : sorted_values) {
4606c3fb27SDimitry Andric       OptionValue *option_value = value.second;
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric       if (one_line)
490b57cec5SDimitry Andric         strm << ' ';
500b57cec5SDimitry Andric       else
510b57cec5SDimitry Andric         strm.EOL();
520b57cec5SDimitry Andric 
5306c3fb27SDimitry Andric       strm.Indent(value.first);
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
560b57cec5SDimitry Andric       switch (dict_type) {
570b57cec5SDimitry Andric       default:
580b57cec5SDimitry Andric       case eTypeArray:
590b57cec5SDimitry Andric       case eTypeDictionary:
600b57cec5SDimitry Andric       case eTypeProperties:
610b57cec5SDimitry Andric       case eTypeFileSpecList:
620b57cec5SDimitry Andric       case eTypePathMap:
630b57cec5SDimitry Andric         strm.PutChar(' ');
640b57cec5SDimitry Andric         option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
650b57cec5SDimitry Andric         break;
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric       case eTypeBoolean:
680b57cec5SDimitry Andric       case eTypeChar:
690b57cec5SDimitry Andric       case eTypeEnum:
70e8d8bef9SDimitry Andric       case eTypeFileLineColumn:
710b57cec5SDimitry Andric       case eTypeFileSpec:
720b57cec5SDimitry Andric       case eTypeFormat:
730b57cec5SDimitry Andric       case eTypeSInt64:
740b57cec5SDimitry Andric       case eTypeString:
750b57cec5SDimitry Andric       case eTypeUInt64:
760b57cec5SDimitry Andric       case eTypeUUID:
770b57cec5SDimitry Andric         // No need to show the type for dictionaries of simple items
780b57cec5SDimitry Andric         strm.PutCString("=");
790b57cec5SDimitry Andric         option_value->DumpValue(exe_ctx, strm,
800b57cec5SDimitry Andric                                 (dump_mask & (~eDumpOptionType)) |
810b57cec5SDimitry Andric                                     extra_dump_options);
820b57cec5SDimitry Andric         break;
830b57cec5SDimitry Andric       }
840b57cec5SDimitry Andric     }
850b57cec5SDimitry Andric     if (!one_line)
860b57cec5SDimitry Andric       strm.IndentLess();
870b57cec5SDimitry Andric   }
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric 
90bdd1243dSDimitry Andric llvm::json::Value
ToJSON(const ExecutionContext * exe_ctx)91bdd1243dSDimitry Andric OptionValueDictionary::ToJSON(const ExecutionContext *exe_ctx) {
92bdd1243dSDimitry Andric   llvm::json::Object dict;
93bdd1243dSDimitry Andric   for (const auto &value : m_values) {
9406c3fb27SDimitry Andric     dict.try_emplace(value.first(), value.second->ToJSON(exe_ctx));
95bdd1243dSDimitry Andric   }
96bdd1243dSDimitry Andric   return dict;
97bdd1243dSDimitry Andric }
98bdd1243dSDimitry Andric 
GetArgs(Args & args) const990b57cec5SDimitry Andric size_t OptionValueDictionary::GetArgs(Args &args) const {
1000b57cec5SDimitry Andric   args.Clear();
10106c3fb27SDimitry Andric   for (const auto &value : m_values) {
1020b57cec5SDimitry Andric     StreamString strm;
10306c3fb27SDimitry Andric     strm.Printf("%s=", value.first().data());
10406c3fb27SDimitry Andric     value.second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
1050b57cec5SDimitry Andric     args.AppendArgument(strm.GetString());
1060b57cec5SDimitry Andric   }
1070b57cec5SDimitry Andric   return args.GetArgumentCount();
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric 
SetArgs(const Args & args,VarSetOperationType op)1100b57cec5SDimitry Andric Status OptionValueDictionary::SetArgs(const Args &args,
1110b57cec5SDimitry Andric                                       VarSetOperationType op) {
1120b57cec5SDimitry Andric   Status error;
1130b57cec5SDimitry Andric   const size_t argc = args.GetArgumentCount();
1140b57cec5SDimitry Andric   switch (op) {
1150b57cec5SDimitry Andric   case eVarSetOperationClear:
1160b57cec5SDimitry Andric     Clear();
1170b57cec5SDimitry Andric     break;
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   case eVarSetOperationAppend:
1200b57cec5SDimitry Andric   case eVarSetOperationReplace:
1210b57cec5SDimitry Andric   case eVarSetOperationAssign:
1220b57cec5SDimitry Andric     if (argc == 0) {
1230b57cec5SDimitry Andric       error.SetErrorString(
1240b57cec5SDimitry Andric           "assign operation takes one or more key=value arguments");
1250b57cec5SDimitry Andric       return error;
1260b57cec5SDimitry Andric     }
1270b57cec5SDimitry Andric     for (const auto &entry : args) {
1289dba64beSDimitry Andric       if (entry.ref().empty()) {
1290b57cec5SDimitry Andric         error.SetErrorString("empty argument");
1300b57cec5SDimitry Andric         return error;
1310b57cec5SDimitry Andric       }
1329dba64beSDimitry Andric       if (!entry.ref().contains('=')) {
1330b57cec5SDimitry Andric         error.SetErrorString(
1340b57cec5SDimitry Andric             "assign operation takes one or more key=value arguments");
1350b57cec5SDimitry Andric         return error;
1360b57cec5SDimitry Andric       }
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric       llvm::StringRef key, value;
1399dba64beSDimitry Andric       std::tie(key, value) = entry.ref().split('=');
1400b57cec5SDimitry Andric       bool key_valid = false;
1410b57cec5SDimitry Andric       if (key.empty()) {
1420b57cec5SDimitry Andric         error.SetErrorString("empty dictionary key");
1430b57cec5SDimitry Andric         return error;
1440b57cec5SDimitry Andric       }
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric       if (key.front() == '[') {
1470b57cec5SDimitry Andric         // Key name starts with '[', so the key value must be in single or
1480b57cec5SDimitry Andric         // double quotes like: ['<key>'] ["<key>"]
1490b57cec5SDimitry Andric         if ((key.size() > 2) && (key.back() == ']')) {
1500b57cec5SDimitry Andric           // Strip leading '[' and trailing ']'
1510b57cec5SDimitry Andric           key = key.substr(1, key.size() - 2);
1520b57cec5SDimitry Andric           const char quote_char = key.front();
1530b57cec5SDimitry Andric           if ((quote_char == '\'') || (quote_char == '"')) {
1540b57cec5SDimitry Andric             if ((key.size() > 2) && (key.back() == quote_char)) {
1550b57cec5SDimitry Andric               // Strip the quotes
1560b57cec5SDimitry Andric               key = key.substr(1, key.size() - 2);
1570b57cec5SDimitry Andric               key_valid = true;
1580b57cec5SDimitry Andric             }
1590b57cec5SDimitry Andric           } else {
1600b57cec5SDimitry Andric             // square brackets, no quotes
1610b57cec5SDimitry Andric             key_valid = true;
1620b57cec5SDimitry Andric           }
1630b57cec5SDimitry Andric         }
1640b57cec5SDimitry Andric       } else {
1650b57cec5SDimitry Andric         // No square brackets or quotes
1660b57cec5SDimitry Andric         key_valid = true;
1670b57cec5SDimitry Andric       }
1680b57cec5SDimitry Andric       if (!key_valid) {
1690b57cec5SDimitry Andric         error.SetErrorStringWithFormat(
1700b57cec5SDimitry Andric             "invalid key \"%s\", the key must be a bare string or "
1710b57cec5SDimitry Andric             "surrounded by brackets with optional quotes: [<key>] or "
1720b57cec5SDimitry Andric             "['<key>'] or [\"<key>\"]",
1730b57cec5SDimitry Andric             key.str().c_str());
1740b57cec5SDimitry Andric         return error;
1750b57cec5SDimitry Andric       }
1760b57cec5SDimitry Andric 
17781ad6265SDimitry Andric       if (m_type_mask == 1u << eTypeEnum) {
17881ad6265SDimitry Andric         auto enum_value =
17981ad6265SDimitry Andric             std::make_shared<OptionValueEnumeration>(m_enum_values, 0);
18081ad6265SDimitry Andric         error = enum_value->SetValueFromString(value);
18181ad6265SDimitry Andric         if (error.Fail())
18281ad6265SDimitry Andric           return error;
18381ad6265SDimitry Andric         m_value_was_set = true;
18406c3fb27SDimitry Andric         SetValueForKey(key, enum_value, true);
18581ad6265SDimitry Andric       } else {
1860b57cec5SDimitry Andric         lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
1870b57cec5SDimitry Andric             value.str().c_str(), m_type_mask, error));
1880b57cec5SDimitry Andric         if (value_sp) {
1890b57cec5SDimitry Andric           if (error.Fail())
1900b57cec5SDimitry Andric             return error;
1910b57cec5SDimitry Andric           m_value_was_set = true;
19206c3fb27SDimitry Andric           SetValueForKey(key, value_sp, true);
1930b57cec5SDimitry Andric         } else {
1940b57cec5SDimitry Andric           error.SetErrorString("dictionaries that can contain multiple types "
1950b57cec5SDimitry Andric                                "must subclass OptionValueArray");
1960b57cec5SDimitry Andric         }
1970b57cec5SDimitry Andric       }
19881ad6265SDimitry Andric     }
1990b57cec5SDimitry Andric     break;
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   case eVarSetOperationRemove:
2020b57cec5SDimitry Andric     if (argc > 0) {
2030b57cec5SDimitry Andric       for (size_t i = 0; i < argc; ++i) {
20406c3fb27SDimitry Andric         llvm::StringRef key(args.GetArgumentAtIndex(i));
2050b57cec5SDimitry Andric         if (!DeleteValueForKey(key)) {
2060b57cec5SDimitry Andric           error.SetErrorStringWithFormat(
2070b57cec5SDimitry Andric               "no value found named '%s', aborting remove operation",
20806c3fb27SDimitry Andric               key.data());
2090b57cec5SDimitry Andric           break;
2100b57cec5SDimitry Andric         }
2110b57cec5SDimitry Andric       }
2120b57cec5SDimitry Andric     } else {
2130b57cec5SDimitry Andric       error.SetErrorString("remove operation takes one or more key arguments");
2140b57cec5SDimitry Andric     }
2150b57cec5SDimitry Andric     break;
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric   case eVarSetOperationInsertBefore:
2180b57cec5SDimitry Andric   case eVarSetOperationInsertAfter:
2190b57cec5SDimitry Andric   case eVarSetOperationInvalid:
2200b57cec5SDimitry Andric     error = OptionValue::SetValueFromString(llvm::StringRef(), op);
2210b57cec5SDimitry Andric     break;
2220b57cec5SDimitry Andric   }
2230b57cec5SDimitry Andric   return error;
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric 
SetValueFromString(llvm::StringRef value,VarSetOperationType op)2260b57cec5SDimitry Andric Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
2270b57cec5SDimitry Andric                                                  VarSetOperationType op) {
2280b57cec5SDimitry Andric   Args args(value.str());
2290b57cec5SDimitry Andric   Status error = SetArgs(args, op);
2300b57cec5SDimitry Andric   if (error.Success())
2310b57cec5SDimitry Andric     NotifyValueChanged();
2320b57cec5SDimitry Andric   return error;
2330b57cec5SDimitry Andric }
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric lldb::OptionValueSP
GetSubValue(const ExecutionContext * exe_ctx,llvm::StringRef name,Status & error) const2360b57cec5SDimitry Andric OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
23706c3fb27SDimitry Andric                                    llvm::StringRef name, Status &error) const {
2380b57cec5SDimitry Andric   lldb::OptionValueSP value_sp;
2390b57cec5SDimitry Andric   if (name.empty())
2400b57cec5SDimitry Andric     return nullptr;
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   llvm::StringRef left, temp;
2430b57cec5SDimitry Andric   std::tie(left, temp) = name.split('[');
2440b57cec5SDimitry Andric   if (left.size() == name.size()) {
2450b57cec5SDimitry Andric     error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
2460b57cec5SDimitry Andric       "support '[<key>]' subvalues where <key> "
2470b57cec5SDimitry Andric       "a string value optionally delimited by "
2480b57cec5SDimitry Andric       "single or double quotes",
2490b57cec5SDimitry Andric       name.str().c_str(), GetTypeAsCString());
2500b57cec5SDimitry Andric     return nullptr;
2510b57cec5SDimitry Andric   }
2520b57cec5SDimitry Andric   assert(!temp.empty());
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   llvm::StringRef key, quote_char;
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric   if (temp[0] == '\"' || temp[0] == '\'') {
2570b57cec5SDimitry Andric     quote_char = temp.take_front();
2580b57cec5SDimitry Andric     temp = temp.drop_front();
2590b57cec5SDimitry Andric   }
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   llvm::StringRef sub_name;
2620b57cec5SDimitry Andric   std::tie(key, sub_name) = temp.split(']');
2630b57cec5SDimitry Andric 
2640b57cec5SDimitry Andric   if (!key.consume_back(quote_char) || key.empty()) {
2650b57cec5SDimitry Andric     error.SetErrorStringWithFormat("invalid value path '%s', "
2660b57cec5SDimitry Andric       "key names must be formatted as ['<key>'] where <key> "
2670b57cec5SDimitry Andric       "is a string that doesn't contain quotes and the quote"
2680b57cec5SDimitry Andric       " char is optional", name.str().c_str());
2690b57cec5SDimitry Andric     return nullptr;
2700b57cec5SDimitry Andric   }
2710b57cec5SDimitry Andric 
27206c3fb27SDimitry Andric   value_sp = GetValueForKey(key);
2730b57cec5SDimitry Andric   if (!value_sp) {
2740b57cec5SDimitry Andric     error.SetErrorStringWithFormat(
2750b57cec5SDimitry Andric       "dictionary does not contain a value for the key name '%s'",
2760b57cec5SDimitry Andric       key.str().c_str());
2770b57cec5SDimitry Andric     return nullptr;
2780b57cec5SDimitry Andric   }
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   if (sub_name.empty())
2810b57cec5SDimitry Andric     return value_sp;
28206c3fb27SDimitry Andric   return value_sp->GetSubValue(exe_ctx, sub_name, error);
2830b57cec5SDimitry Andric }
2840b57cec5SDimitry Andric 
SetSubValue(const ExecutionContext * exe_ctx,VarSetOperationType op,llvm::StringRef name,llvm::StringRef value)2850b57cec5SDimitry Andric Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
2860b57cec5SDimitry Andric                                           VarSetOperationType op,
2870b57cec5SDimitry Andric                                           llvm::StringRef name,
2880b57cec5SDimitry Andric                                           llvm::StringRef value) {
2890b57cec5SDimitry Andric   Status error;
29006c3fb27SDimitry Andric   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error));
2910b57cec5SDimitry Andric   if (value_sp)
2920b57cec5SDimitry Andric     error = value_sp->SetValueFromString(value, op);
2930b57cec5SDimitry Andric   else {
2940b57cec5SDimitry Andric     if (error.AsCString() == nullptr)
2950b57cec5SDimitry Andric       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
2960b57cec5SDimitry Andric   }
2970b57cec5SDimitry Andric   return error;
2980b57cec5SDimitry Andric }
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric lldb::OptionValueSP
GetValueForKey(llvm::StringRef key) const30106c3fb27SDimitry Andric OptionValueDictionary::GetValueForKey(llvm::StringRef key) const {
3020b57cec5SDimitry Andric   lldb::OptionValueSP value_sp;
30306c3fb27SDimitry Andric   auto pos = m_values.find(key);
3040b57cec5SDimitry Andric   if (pos != m_values.end())
3050b57cec5SDimitry Andric     value_sp = pos->second;
3060b57cec5SDimitry Andric   return value_sp;
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
SetValueForKey(llvm::StringRef key,const lldb::OptionValueSP & value_sp,bool can_replace)30906c3fb27SDimitry Andric bool OptionValueDictionary::SetValueForKey(llvm::StringRef key,
3100b57cec5SDimitry Andric                                            const lldb::OptionValueSP &value_sp,
3110b57cec5SDimitry Andric                                            bool can_replace) {
3120b57cec5SDimitry Andric   // Make sure the value_sp object is allowed to contain values of the type
3130b57cec5SDimitry Andric   // passed in...
3140b57cec5SDimitry Andric   if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
3150b57cec5SDimitry Andric     if (!can_replace) {
31606c3fb27SDimitry Andric       auto pos = m_values.find(key);
3170b57cec5SDimitry Andric       if (pos != m_values.end())
3180b57cec5SDimitry Andric         return false;
3190b57cec5SDimitry Andric     }
3200b57cec5SDimitry Andric     m_values[key] = value_sp;
3210b57cec5SDimitry Andric     return true;
3220b57cec5SDimitry Andric   }
3230b57cec5SDimitry Andric   return false;
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
DeleteValueForKey(llvm::StringRef key)32606c3fb27SDimitry Andric bool OptionValueDictionary::DeleteValueForKey(llvm::StringRef key) {
32706c3fb27SDimitry Andric   auto pos = m_values.find(key);
3280b57cec5SDimitry Andric   if (pos != m_values.end()) {
3290b57cec5SDimitry Andric     m_values.erase(pos);
3300b57cec5SDimitry Andric     return true;
3310b57cec5SDimitry Andric   }
3320b57cec5SDimitry Andric   return false;
3330b57cec5SDimitry Andric }
3340b57cec5SDimitry Andric 
335fe6060f1SDimitry Andric OptionValueSP
DeepCopy(const OptionValueSP & new_parent) const336fe6060f1SDimitry Andric OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const {
337fe6060f1SDimitry Andric   auto copy_sp = OptionValue::DeepCopy(new_parent);
338fe6060f1SDimitry Andric   // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived
339fe6060f1SDimitry Andric   // types that override GetType returning a different value.
340fe6060f1SDimitry Andric   auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get());
341fe6060f1SDimitry Andric   lldbassert(dict_value_ptr);
342fe6060f1SDimitry Andric 
343fe6060f1SDimitry Andric   for (auto &value : dict_value_ptr->m_values)
344fe6060f1SDimitry Andric     value.second = value.second->DeepCopy(copy_sp);
345fe6060f1SDimitry Andric 
346fe6060f1SDimitry Andric   return copy_sp;
3470b57cec5SDimitry Andric }
348