Path: blob/main/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp
39587 views
//===-- OptionValueDictionary.cpp -----------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Interpreter/OptionValueDictionary.h"910#include "lldb/DataFormatters/FormatManager.h"11#include "lldb/Interpreter/OptionValueEnumeration.h"12#include "lldb/Interpreter/OptionValueString.h"13#include "lldb/Utility/Args.h"14#include "lldb/Utility/State.h"15#include "llvm/ADT/StringRef.h"1617using namespace lldb;18using namespace lldb_private;1920void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,21Stream &strm, uint32_t dump_mask) {22const Type dict_type = ConvertTypeMaskToType(m_type_mask);23if (dump_mask & eDumpOptionType) {24if (m_type_mask != eTypeInvalid)25strm.Printf("(%s of %ss)", GetTypeAsCString(),26GetBuiltinTypeAsCString(dict_type));27else28strm.Printf("(%s)", GetTypeAsCString());29}30if (dump_mask & eDumpOptionValue) {31const bool one_line = dump_mask & eDumpOptionCommand;32if (dump_mask & eDumpOptionType)33strm.PutCString(" =");3435if (!one_line)36strm.IndentMore();3738// m_values is not guaranteed to be sorted alphabetically, so for39// consistentcy we will sort them here before dumping40std::map<llvm::StringRef, OptionValue *> sorted_values;41for (const auto &value : m_values) {42sorted_values[value.first()] = value.second.get();43}44for (const auto &value : sorted_values) {45OptionValue *option_value = value.second;4647if (one_line)48strm << ' ';49else50strm.EOL();5152strm.Indent(value.first);5354const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;55switch (dict_type) {56default:57case eTypeArray:58case eTypeDictionary:59case eTypeProperties:60case eTypeFileSpecList:61case eTypePathMap:62strm.PutChar(' ');63option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);64break;6566case eTypeBoolean:67case eTypeChar:68case eTypeEnum:69case eTypeFileLineColumn:70case eTypeFileSpec:71case eTypeFormat:72case eTypeSInt64:73case eTypeString:74case eTypeUInt64:75case eTypeUUID:76// No need to show the type for dictionaries of simple items77strm.PutCString("=");78option_value->DumpValue(exe_ctx, strm,79(dump_mask & (~eDumpOptionType)) |80extra_dump_options);81break;82}83}84if (!one_line)85strm.IndentLess();86}87}8889llvm::json::Value90OptionValueDictionary::ToJSON(const ExecutionContext *exe_ctx) {91llvm::json::Object dict;92for (const auto &value : m_values) {93dict.try_emplace(value.first(), value.second->ToJSON(exe_ctx));94}95return dict;96}9798size_t OptionValueDictionary::GetArgs(Args &args) const {99args.Clear();100for (const auto &value : m_values) {101StreamString strm;102strm.Printf("%s=", value.first().data());103value.second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);104args.AppendArgument(strm.GetString());105}106return args.GetArgumentCount();107}108109Status OptionValueDictionary::SetArgs(const Args &args,110VarSetOperationType op) {111Status error;112const size_t argc = args.GetArgumentCount();113switch (op) {114case eVarSetOperationClear:115Clear();116break;117118case eVarSetOperationAppend:119case eVarSetOperationReplace:120case eVarSetOperationAssign:121if (argc == 0) {122error.SetErrorString(123"assign operation takes one or more key=value arguments");124return error;125}126for (const auto &entry : args) {127if (entry.ref().empty()) {128error.SetErrorString("empty argument");129return error;130}131if (!entry.ref().contains('=')) {132error.SetErrorString(133"assign operation takes one or more key=value arguments");134return error;135}136137llvm::StringRef key, value;138std::tie(key, value) = entry.ref().split('=');139bool key_valid = false;140if (key.empty()) {141error.SetErrorString("empty dictionary key");142return error;143}144145if (key.front() == '[') {146// Key name starts with '[', so the key value must be in single or147// double quotes like: ['<key>'] ["<key>"]148if ((key.size() > 2) && (key.back() == ']')) {149// Strip leading '[' and trailing ']'150key = key.substr(1, key.size() - 2);151const char quote_char = key.front();152if ((quote_char == '\'') || (quote_char == '"')) {153if ((key.size() > 2) && (key.back() == quote_char)) {154// Strip the quotes155key = key.substr(1, key.size() - 2);156key_valid = true;157}158} else {159// square brackets, no quotes160key_valid = true;161}162}163} else {164// No square brackets or quotes165key_valid = true;166}167if (!key_valid) {168error.SetErrorStringWithFormat(169"invalid key \"%s\", the key must be a bare string or "170"surrounded by brackets with optional quotes: [<key>] or "171"['<key>'] or [\"<key>\"]",172key.str().c_str());173return error;174}175176if (m_type_mask == 1u << eTypeEnum) {177auto enum_value =178std::make_shared<OptionValueEnumeration>(m_enum_values, 0);179error = enum_value->SetValueFromString(value);180if (error.Fail())181return error;182m_value_was_set = true;183SetValueForKey(key, enum_value, true);184} else {185lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(186value.str().c_str(), m_type_mask, error));187if (value_sp) {188if (error.Fail())189return error;190m_value_was_set = true;191SetValueForKey(key, value_sp, true);192} else {193error.SetErrorString("dictionaries that can contain multiple types "194"must subclass OptionValueArray");195}196}197}198break;199200case eVarSetOperationRemove:201if (argc > 0) {202for (size_t i = 0; i < argc; ++i) {203llvm::StringRef key(args.GetArgumentAtIndex(i));204if (!DeleteValueForKey(key)) {205error.SetErrorStringWithFormat(206"no value found named '%s', aborting remove operation",207key.data());208break;209}210}211} else {212error.SetErrorString("remove operation takes one or more key arguments");213}214break;215216case eVarSetOperationInsertBefore:217case eVarSetOperationInsertAfter:218case eVarSetOperationInvalid:219error = OptionValue::SetValueFromString(llvm::StringRef(), op);220break;221}222return error;223}224225Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,226VarSetOperationType op) {227Args args(value.str());228Status error = SetArgs(args, op);229if (error.Success())230NotifyValueChanged();231return error;232}233234lldb::OptionValueSP235OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,236llvm::StringRef name, Status &error) const {237lldb::OptionValueSP value_sp;238if (name.empty())239return nullptr;240241llvm::StringRef left, temp;242std::tie(left, temp) = name.split('[');243if (left.size() == name.size()) {244error.SetErrorStringWithFormat("invalid value path '%s', %s values only "245"support '[<key>]' subvalues where <key> "246"a string value optionally delimited by "247"single or double quotes",248name.str().c_str(), GetTypeAsCString());249return nullptr;250}251assert(!temp.empty());252253llvm::StringRef key, quote_char;254255if (temp[0] == '\"' || temp[0] == '\'') {256quote_char = temp.take_front();257temp = temp.drop_front();258}259260llvm::StringRef sub_name;261std::tie(key, sub_name) = temp.split(']');262263if (!key.consume_back(quote_char) || key.empty()) {264error.SetErrorStringWithFormat("invalid value path '%s', "265"key names must be formatted as ['<key>'] where <key> "266"is a string that doesn't contain quotes and the quote"267" char is optional", name.str().c_str());268return nullptr;269}270271value_sp = GetValueForKey(key);272if (!value_sp) {273error.SetErrorStringWithFormat(274"dictionary does not contain a value for the key name '%s'",275key.str().c_str());276return nullptr;277}278279if (sub_name.empty())280return value_sp;281return value_sp->GetSubValue(exe_ctx, sub_name, error);282}283284Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,285VarSetOperationType op,286llvm::StringRef name,287llvm::StringRef value) {288Status error;289lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error));290if (value_sp)291error = value_sp->SetValueFromString(value, op);292else {293if (error.AsCString() == nullptr)294error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());295}296return error;297}298299lldb::OptionValueSP300OptionValueDictionary::GetValueForKey(llvm::StringRef key) const {301lldb::OptionValueSP value_sp;302auto pos = m_values.find(key);303if (pos != m_values.end())304value_sp = pos->second;305return value_sp;306}307308bool OptionValueDictionary::SetValueForKey(llvm::StringRef key,309const lldb::OptionValueSP &value_sp,310bool can_replace) {311// Make sure the value_sp object is allowed to contain values of the type312// passed in...313if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {314if (!can_replace) {315auto pos = m_values.find(key);316if (pos != m_values.end())317return false;318}319m_values[key] = value_sp;320return true;321}322return false;323}324325bool OptionValueDictionary::DeleteValueForKey(llvm::StringRef key) {326auto pos = m_values.find(key);327if (pos != m_values.end()) {328m_values.erase(pos);329return true;330}331return false;332}333334OptionValueSP335OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const {336auto copy_sp = OptionValue::DeepCopy(new_parent);337// copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived338// types that override GetType returning a different value.339auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get());340lldbassert(dict_value_ptr);341342for (auto &value : dict_value_ptr->m_values)343value.second = value.second->DeepCopy(copy_sp);344345return copy_sp;346}347348349