| //===-- OptionValueArray.cpp ----------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Interpreter/OptionValueArray.h" | |
| #include "lldb/Interpreter/OptionValue.h" | |
| #include "lldb/Utility/Args.h" | |
| #include "lldb/Utility/Stream.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, | |
| uint32_t dump_mask) { | |
| const Type array_element_type = ConvertTypeMaskToType(m_type_mask); | |
| if (dump_mask & eDumpOptionType) { | |
| if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) | |
| strm.Printf("(%s of %ss)", GetTypeAsCString(), | |
| GetBuiltinTypeAsCString(array_element_type)); | |
| else | |
| strm.Printf("(%s)", GetTypeAsCString()); | |
| } | |
| if (dump_mask & eDumpOptionValue) { | |
| const bool one_line = dump_mask & eDumpOptionCommand; | |
| const uint32_t size = m_values.size(); | |
| if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { | |
| strm.PutCString(" ="); | |
| if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) { | |
| DefaultValueFormat label(strm); | |
| strm.PutCString("empty"); | |
| } | |
| if (!m_values.empty() && !one_line) | |
| strm.PutCString("\n"); | |
| } | |
| if (!one_line) | |
| strm.IndentMore(); | |
| for (uint32_t i = 0; i < size; ++i) { | |
| if (!one_line) { | |
| strm.Indent(); | |
| strm.Printf("[%u]: ", i); | |
| } | |
| const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; | |
| switch (array_element_type) { | |
| default: | |
| case eTypeArray: | |
| case eTypeDictionary: | |
| case eTypeProperties: | |
| case eTypeFileSpecList: | |
| case eTypePathMap: | |
| m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); | |
| break; | |
| case eTypeBoolean: | |
| case eTypeChar: | |
| case eTypeEnum: | |
| case eTypeFileSpec: | |
| case eTypeFileLineColumn: | |
| case eTypeFormat: | |
| case eTypeSInt64: | |
| case eTypeString: | |
| case eTypeUInt64: | |
| case eTypeUUID: | |
| // No need to show the type for dictionaries of simple items | |
| m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | | |
| extra_dump_options); | |
| break; | |
| } | |
| if (!one_line) { | |
| if (i < (size - 1)) | |
| strm.EOL(); | |
| } else { | |
| strm << ' '; | |
| } | |
| } | |
| if (!one_line) | |
| strm.IndentLess(); | |
| } | |
| } | |
| llvm::json::Value | |
| OptionValueArray::ToJSON(const ExecutionContext *exe_ctx) const { | |
| llvm::json::Array json_array; | |
| const uint32_t size = m_values.size(); | |
| for (uint32_t i = 0; i < size; ++i) | |
| json_array.emplace_back(m_values[i]->ToJSON(exe_ctx)); | |
| return json_array; | |
| } | |
| Status OptionValueArray::SetValueFromString(llvm::StringRef value, | |
| VarSetOperationType op) { | |
| Args args(value.str()); | |
| Status error = SetArgs(args, op); | |
| if (error.Success()) | |
| NotifyValueChanged(); | |
| return error; | |
| } | |
| lldb::OptionValueSP | |
| OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx, | |
| llvm::StringRef name, Status &error) const { | |
| if (name.empty() || name.front() != '[') { | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid value path '%s', %s values only support '[<index>]' subvalues " | |
| "where <index> is a positive or negative array index", | |
| name.str().c_str(), GetTypeAsCString()); | |
| return nullptr; | |
| } | |
| name = name.drop_front(); | |
| llvm::StringRef index, sub_value; | |
| std::tie(index, sub_value) = name.split(']'); | |
| if (index.size() == name.size()) { | |
| // Couldn't find a closing bracket | |
| return nullptr; | |
| } | |
| const size_t array_count = m_values.size(); | |
| int32_t idx = 0; | |
| if (index.getAsInteger(0, idx)) | |
| return nullptr; | |
| uint32_t new_idx = UINT32_MAX; | |
| if (idx < 0) { | |
| // Access from the end of the array if the index is negative | |
| new_idx = array_count - idx; | |
| } else { | |
| // Just a standard index | |
| new_idx = idx; | |
| } | |
| if (new_idx < array_count) { | |
| if (m_values[new_idx]) { | |
| if (!sub_value.empty()) | |
| return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, error); | |
| else | |
| return m_values[new_idx]; | |
| } | |
| } else { | |
| if (array_count == 0) | |
| error = Status::FromErrorStringWithFormat( | |
| "index %i is not valid for an empty array", idx); | |
| else if (idx > 0) | |
| error = Status::FromErrorStringWithFormat( | |
| "index %i out of range, valid values are 0 through %" PRIu64, idx, | |
| (uint64_t)(array_count - 1)); | |
| else | |
| error = | |
| Status::FromErrorStringWithFormat("negative index %i out of range, " | |
| "valid values are -1 through " | |
| "-%" PRIu64, | |
| idx, (uint64_t)array_count); | |
| } | |
| return OptionValueSP(); | |
| } | |
| size_t OptionValueArray::GetArgs(Args &args) const { | |
| args.Clear(); | |
| const uint32_t size = m_values.size(); | |
| for (uint32_t i = 0; i < size; ++i) { | |
| auto string_value = m_values[i]->GetValueAs<llvm::StringRef>(); | |
| if (string_value) | |
| args.AppendArgument(*string_value); | |
| } | |
| return args.GetArgumentCount(); | |
| } | |
| Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { | |
| Status error; | |
| const size_t argc = args.GetArgumentCount(); | |
| switch (op) { | |
| case eVarSetOperationInvalid: | |
| error = Status::FromErrorString("unsupported operation"); | |
| break; | |
| case eVarSetOperationInsertBefore: | |
| case eVarSetOperationInsertAfter: | |
| if (argc > 1) { | |
| uint32_t idx; | |
| const uint32_t count = GetSize(); | |
| if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid insert array index %s, index must be 0 through %u", | |
| args.GetArgumentAtIndex(0), count); | |
| } else { | |
| if (op == eVarSetOperationInsertAfter) | |
| ++idx; | |
| for (size_t i = 1; i < argc; ++i, ++idx) { | |
| lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( | |
| args.GetArgumentAtIndex(i), m_type_mask, error)); | |
| if (value_sp) { | |
| if (error.Fail()) | |
| return error; | |
| if (idx >= m_values.size()) | |
| m_values.push_back(value_sp); | |
| else | |
| m_values.insert(m_values.begin() + idx, value_sp); | |
| } else { | |
| error = Status::FromErrorString( | |
| "array of complex types must subclass OptionValueArray"); | |
| return error; | |
| } | |
| } | |
| } | |
| } else { | |
| error = Status::FromErrorString( | |
| "insert operation takes an array index followed by " | |
| "one or more values"); | |
| } | |
| break; | |
| case eVarSetOperationRemove: | |
| if (argc > 0) { | |
| const uint32_t size = m_values.size(); | |
| std::vector<int> remove_indexes; | |
| bool all_indexes_valid = true; | |
| size_t i; | |
| for (i = 0; i < argc; ++i) { | |
| size_t idx; | |
| if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) { | |
| all_indexes_valid = false; | |
| break; | |
| } else | |
| remove_indexes.push_back(idx); | |
| } | |
| if (all_indexes_valid) { | |
| size_t num_remove_indexes = remove_indexes.size(); | |
| if (num_remove_indexes) { | |
| // Sort and then erase in reverse so indexes are always valid | |
| if (num_remove_indexes > 1) { | |
| llvm::sort(remove_indexes); | |
| for (std::vector<int>::const_reverse_iterator | |
| pos = remove_indexes.rbegin(), | |
| end = remove_indexes.rend(); | |
| pos != end; ++pos) { | |
| m_values.erase(m_values.begin() + *pos); | |
| } | |
| } else { | |
| // Only one index | |
| m_values.erase(m_values.begin() + remove_indexes.front()); | |
| } | |
| } | |
| } else { | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid array index '%s', aborting remove operation", | |
| args.GetArgumentAtIndex(i)); | |
| } | |
| } else { | |
| error = Status::FromErrorString( | |
| "remove operation takes one or more array indices"); | |
| } | |
| break; | |
| case eVarSetOperationClear: | |
| Clear(); | |
| break; | |
| case eVarSetOperationReplace: | |
| if (argc > 1) { | |
| uint32_t idx; | |
| const uint32_t count = GetSize(); | |
| if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid replace array index %s, index must be 0 through %u", | |
| args.GetArgumentAtIndex(0), count); | |
| } else { | |
| for (size_t i = 1; i < argc; ++i, ++idx) { | |
| lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( | |
| args.GetArgumentAtIndex(i), m_type_mask, error)); | |
| if (value_sp) { | |
| if (error.Fail()) | |
| return error; | |
| if (idx < count) | |
| m_values[idx] = value_sp; | |
| else | |
| m_values.push_back(value_sp); | |
| } else { | |
| error = Status::FromErrorString( | |
| "array of complex types must subclass OptionValueArray"); | |
| return error; | |
| } | |
| } | |
| } | |
| } else { | |
| error = Status::FromErrorString( | |
| "replace operation takes an array index followed by " | |
| "one or more values"); | |
| } | |
| break; | |
| case eVarSetOperationAssign: | |
| m_values.clear(); | |
| // Fall through to append case | |
| [[fallthrough]]; | |
| case eVarSetOperationAppend: | |
| for (size_t i = 0; i < argc; ++i) { | |
| lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( | |
| args.GetArgumentAtIndex(i), m_type_mask, error)); | |
| if (value_sp) { | |
| if (error.Fail()) | |
| return error; | |
| m_value_was_set = true; | |
| AppendValue(value_sp); | |
| } else { | |
| error = Status::FromErrorString( | |
| "array of complex types must subclass OptionValueArray"); | |
| } | |
| } | |
| break; | |
| } | |
| return error; | |
| } | |
| OptionValueSP | |
| OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { | |
| auto copy_sp = OptionValue::DeepCopy(new_parent); | |
| // copy_sp->GetAsArray cannot be used here as it doesn't work for derived | |
| // types that override GetType returning a different value. | |
| auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); | |
| lldbassert(array_value_ptr); | |
| for (auto &value : array_value_ptr->m_values) | |
| value = value->DeepCopy(copy_sp); | |
| return copy_sp; | |
| } |