| //===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "lldb/Interpreter/OptionValueDictionary.h" | 
 |  | 
 | // C Includes | 
 | // C++ Includes | 
 | // Other libraries and framework includes | 
 | #include "llvm/ADT/StringRef.h" | 
 | // Project includes | 
 | #include "lldb/Core/State.h" | 
 | #include "lldb/DataFormatters/FormatManager.h" | 
 | #include "lldb/Interpreter/OptionValueString.h" | 
 | #include "lldb/Utility/Args.h" | 
 |  | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 |  | 
 | void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, | 
 |                                       Stream &strm, uint32_t dump_mask) { | 
 |   const Type dict_type = ConvertTypeMaskToType(m_type_mask); | 
 |   if (dump_mask & eDumpOptionType) { | 
 |     if (m_type_mask != eTypeInvalid) | 
 |       strm.Printf("(%s of %ss)", GetTypeAsCString(), | 
 |                   GetBuiltinTypeAsCString(dict_type)); | 
 |     else | 
 |       strm.Printf("(%s)", GetTypeAsCString()); | 
 |   } | 
 |   if (dump_mask & eDumpOptionValue) { | 
 |     if (dump_mask & eDumpOptionType) | 
 |       strm.PutCString(" ="); | 
 |  | 
 |     collection::iterator pos, end = m_values.end(); | 
 |  | 
 |     strm.IndentMore(); | 
 |  | 
 |     for (pos = m_values.begin(); pos != end; ++pos) { | 
 |       OptionValue *option_value = pos->second.get(); | 
 |       strm.EOL(); | 
 |       strm.Indent(pos->first.GetCString()); | 
 |  | 
 |       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; | 
 |       switch (dict_type) { | 
 |       default: | 
 |       case eTypeArray: | 
 |       case eTypeDictionary: | 
 |       case eTypeProperties: | 
 |       case eTypeFileSpecList: | 
 |       case eTypePathMap: | 
 |         strm.PutChar(' '); | 
 |         option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); | 
 |         break; | 
 |  | 
 |       case eTypeBoolean: | 
 |       case eTypeChar: | 
 |       case eTypeEnum: | 
 |       case eTypeFileSpec: | 
 |       case eTypeFormat: | 
 |       case eTypeSInt64: | 
 |       case eTypeString: | 
 |       case eTypeUInt64: | 
 |       case eTypeUUID: | 
 |         // No need to show the type for dictionaries of simple items | 
 |         strm.PutCString("="); | 
 |         option_value->DumpValue(exe_ctx, strm, | 
 |                                 (dump_mask & (~eDumpOptionType)) | | 
 |                                     extra_dump_options); | 
 |         break; | 
 |       } | 
 |     } | 
 |     strm.IndentLess(); | 
 |   } | 
 | } | 
 |  | 
 | size_t OptionValueDictionary::GetArgs(Args &args) const { | 
 |   args.Clear(); | 
 |   collection::const_iterator pos, end = m_values.end(); | 
 |   for (pos = m_values.begin(); pos != end; ++pos) { | 
 |     StreamString strm; | 
 |     strm.Printf("%s=", pos->first.GetCString()); | 
 |     pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw); | 
 |     args.AppendArgument(strm.GetString()); | 
 |   } | 
 |   return args.GetArgumentCount(); | 
 | } | 
 |  | 
 | Status OptionValueDictionary::SetArgs(const Args &args, | 
 |                                       VarSetOperationType op) { | 
 |   Status error; | 
 |   const size_t argc = args.GetArgumentCount(); | 
 |   switch (op) { | 
 |   case eVarSetOperationClear: | 
 |     Clear(); | 
 |     break; | 
 |  | 
 |   case eVarSetOperationAppend: | 
 |   case eVarSetOperationReplace: | 
 |   case eVarSetOperationAssign: | 
 |     if (argc == 0) { | 
 |       error.SetErrorString( | 
 |           "assign operation takes one or more key=value arguments"); | 
 |       return error; | 
 |     } | 
 |     for (const auto &entry : args) { | 
 |       if (entry.ref.empty()) { | 
 |         error.SetErrorString("empty argument"); | 
 |         return error; | 
 |       } | 
 |       if (!entry.ref.contains('=')) { | 
 |         error.SetErrorString( | 
 |             "assign operation takes one or more key=value arguments"); | 
 |         return error; | 
 |       } | 
 |  | 
 |       llvm::StringRef key, value; | 
 |       std::tie(key, value) = entry.ref.split('='); | 
 |       bool key_valid = false; | 
 |       if (key.empty()) { | 
 |         error.SetErrorString("empty dictionary key"); | 
 |         return error; | 
 |       } | 
 |  | 
 |       if (key.front() == '[') { | 
 |         // Key name starts with '[', so the key value must be in single or | 
 |         // double quotes like: ['<key>'] ["<key>"] | 
 |         if ((key.size() > 2) && (key.back() == ']')) { | 
 |           // Strip leading '[' and trailing ']' | 
 |           key = key.substr(1, key.size() - 2); | 
 |           const char quote_char = key.front(); | 
 |           if ((quote_char == '\'') || (quote_char == '"')) { | 
 |             if ((key.size() > 2) && (key.back() == quote_char)) { | 
 |               // Strip the quotes | 
 |               key = key.substr(1, key.size() - 2); | 
 |               key_valid = true; | 
 |             } | 
 |           } else { | 
 |             // square brackets, no quotes | 
 |             key_valid = true; | 
 |           } | 
 |         } | 
 |       } else { | 
 |         // No square brackets or quotes | 
 |         key_valid = true; | 
 |       } | 
 |       if (!key_valid) { | 
 |         error.SetErrorStringWithFormat( | 
 |             "invalid key \"%s\", the key must be a bare string or " | 
 |             "surrounded by brackets with optional quotes: [<key>] or " | 
 |             "['<key>'] or [\"<key>\"]", | 
 |             key.str().c_str()); | 
 |         return error; | 
 |       } | 
 |  | 
 |       lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( | 
 |           value.str().c_str(), m_type_mask, error)); | 
 |       if (value_sp) { | 
 |         if (error.Fail()) | 
 |           return error; | 
 |         m_value_was_set = true; | 
 |         SetValueForKey(ConstString(key), value_sp, true); | 
 |       } else { | 
 |         error.SetErrorString("dictionaries that can contain multiple types " | 
 |                              "must subclass OptionValueArray"); | 
 |       } | 
 |     } | 
 |     break; | 
 |  | 
 |   case eVarSetOperationRemove: | 
 |     if (argc > 0) { | 
 |       for (size_t i = 0; i < argc; ++i) { | 
 |         ConstString key(args.GetArgumentAtIndex(i)); | 
 |         if (!DeleteValueForKey(key)) { | 
 |           error.SetErrorStringWithFormat( | 
 |               "no value found named '%s', aborting remove operation", | 
 |               key.GetCString()); | 
 |           break; | 
 |         } | 
 |       } | 
 |     } else { | 
 |       error.SetErrorString("remove operation takes one or more key arguments"); | 
 |     } | 
 |     break; | 
 |  | 
 |   case eVarSetOperationInsertBefore: | 
 |   case eVarSetOperationInsertAfter: | 
 |   case eVarSetOperationInvalid: | 
 |     error = OptionValue::SetValueFromString(llvm::StringRef(), op); | 
 |     break; | 
 |   } | 
 |   return error; | 
 | } | 
 |  | 
 | Status OptionValueDictionary::SetValueFromString(llvm::StringRef value, | 
 |                                                  VarSetOperationType op) { | 
 |   Args args(value.str()); | 
 |   Status error = SetArgs(args, op); | 
 |   if (error.Success()) | 
 |     NotifyValueChanged(); | 
 |   return error; | 
 | } | 
 |  | 
 | lldb::OptionValueSP | 
 | OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx, | 
 |                                    llvm::StringRef name, bool will_modify, | 
 |                                    Status &error) const { | 
 |   lldb::OptionValueSP value_sp; | 
 |   if (name.empty()) | 
 |     return nullptr; | 
 |  | 
 |   llvm::StringRef left, temp; | 
 |   std::tie(left, temp) = name.split('['); | 
 |   if (left.size() == name.size()) { | 
 |     error.SetErrorStringWithFormat("invalid value path '%s', %s values only " | 
 |       "support '[<key>]' subvalues where <key> " | 
 |       "a string value optionally delimited by " | 
 |       "single or double quotes", | 
 |       name.str().c_str(), GetTypeAsCString()); | 
 |     return nullptr; | 
 |   } | 
 |   assert(!temp.empty()); | 
 |  | 
 |   llvm::StringRef key, quote_char; | 
 |  | 
 |   if (temp[0] == '\"' || temp[0] == '\'') { | 
 |     quote_char = temp.take_front(); | 
 |     temp = temp.drop_front(); | 
 |   } | 
 |  | 
 |   llvm::StringRef sub_name; | 
 |   std::tie(key, sub_name) = temp.split(']'); | 
 |  | 
 |   if (!key.consume_back(quote_char) || key.empty()) { | 
 |     error.SetErrorStringWithFormat("invalid value path '%s', " | 
 |       "key names must be formatted as ['<key>'] where <key> " | 
 |       "is a string that doesn't contain quotes and the quote" | 
 |       " char is optional", name.str().c_str()); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   value_sp = GetValueForKey(ConstString(key)); | 
 |   if (!value_sp) { | 
 |     error.SetErrorStringWithFormat( | 
 |       "dictionary does not contain a value for the key name '%s'", | 
 |       key.str().c_str()); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   if (sub_name.empty()) | 
 |     return value_sp; | 
 |   return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error); | 
 | } | 
 |  | 
 | Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx, | 
 |                                           VarSetOperationType op, | 
 |                                           llvm::StringRef name, | 
 |                                           llvm::StringRef value) { | 
 |   Status error; | 
 |   const bool will_modify = true; | 
 |   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error)); | 
 |   if (value_sp) | 
 |     error = value_sp->SetValueFromString(value, op); | 
 |   else { | 
 |     if (error.AsCString() == nullptr) | 
 |       error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str()); | 
 |   } | 
 |   return error; | 
 | } | 
 |  | 
 | lldb::OptionValueSP | 
 | OptionValueDictionary::GetValueForKey(const ConstString &key) const { | 
 |   lldb::OptionValueSP value_sp; | 
 |   collection::const_iterator pos = m_values.find(key); | 
 |   if (pos != m_values.end()) | 
 |     value_sp = pos->second; | 
 |   return value_sp; | 
 | } | 
 |  | 
 | bool OptionValueDictionary::SetValueForKey(const ConstString &key, | 
 |                                            const lldb::OptionValueSP &value_sp, | 
 |                                            bool can_replace) { | 
 |   // Make sure the value_sp object is allowed to contain values of the type | 
 |   // passed in... | 
 |   if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) { | 
 |     if (!can_replace) { | 
 |       collection::const_iterator pos = m_values.find(key); | 
 |       if (pos != m_values.end()) | 
 |         return false; | 
 |     } | 
 |     m_values[key] = value_sp; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool OptionValueDictionary::DeleteValueForKey(const ConstString &key) { | 
 |   collection::iterator pos = m_values.find(key); | 
 |   if (pos != m_values.end()) { | 
 |     m_values.erase(pos); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | lldb::OptionValueSP OptionValueDictionary::DeepCopy() const { | 
 |   OptionValueDictionary *copied_dict = | 
 |       new OptionValueDictionary(m_type_mask, m_raw_value_dump); | 
 |   lldb::OptionValueSP copied_value_sp(copied_dict); | 
 |   collection::const_iterator pos, end = m_values.end(); | 
 |   for (pos = m_values.begin(); pos != end; ++pos) { | 
 |     StreamString strm; | 
 |     strm.Printf("%s=", pos->first.GetCString()); | 
 |     copied_dict->SetValueForKey(pos->first, pos->second->DeepCopy(), true); | 
 |   } | 
 |   return copied_value_sp; | 
 | } |