forked from OSchip/llvm-project
315 lines
10 KiB
C++
315 lines
10 KiB
C++
//===-- 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/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)
|
|
strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\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();
|
|
}
|
|
}
|
|
|
|
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, bool will_modify,
|
|
Status &error) const {
|
|
if (name.empty() || name.front() != '[') {
|
|
error.SetErrorStringWithFormat(
|
|
"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,
|
|
will_modify, error);
|
|
else
|
|
return m_values[new_idx];
|
|
}
|
|
} else {
|
|
if (array_count == 0)
|
|
error.SetErrorStringWithFormat(
|
|
"index %i is not valid for an empty array", idx);
|
|
else if (idx > 0)
|
|
error.SetErrorStringWithFormat(
|
|
"index %i out of range, valid values are 0 through %" PRIu64,
|
|
idx, (uint64_t)(array_count - 1));
|
|
else
|
|
error.SetErrorStringWithFormat("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) {
|
|
llvm::StringRef string_value = m_values[i]->GetStringValue();
|
|
if (!string_value.empty())
|
|
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.SetErrorString("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.SetErrorStringWithFormat(
|
|
"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.SetErrorString(
|
|
"array of complex types must subclass OptionValueArray");
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error.SetErrorString("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.begin(), remove_indexes.end());
|
|
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.SetErrorStringWithFormat(
|
|
"invalid array index '%s', aborting remove operation",
|
|
args.GetArgumentAtIndex(i));
|
|
}
|
|
} else {
|
|
error.SetErrorString("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.SetErrorStringWithFormat(
|
|
"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.SetErrorString(
|
|
"array of complex types must subclass OptionValueArray");
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error.SetErrorString("replace operation takes an array index followed by "
|
|
"one or more values");
|
|
}
|
|
break;
|
|
|
|
case eVarSetOperationAssign:
|
|
m_values.clear();
|
|
// Fall through to append case
|
|
LLVM_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.SetErrorString(
|
|
"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;
|
|
}
|