Some implementations of the DeepCopy function called the copy constructor that copied m_parent member instead of setting a new parent. Others just leaved the base class's members (m_parent, m_callback, m_was_set) empty.
One more problem is that not all classes override this function, e.g. OptionValueArgs::DeepCopy produces OptionValueArray instance, and Target[Process/Thread]ValueProperty::DeepCopy produces OptionValueProperty. This makes downcasting via static_cast invalid.
The patch implements idiom "virtual constructor" to fix these issues.
Add a test that checks DeepCopy for correct copying/setting all data members of the base class.
Differential Revision: https://reviews.llvm.org/D96952
#define LLDB_INTERPRETER_OPTIONVALUE_H
#include "lldb/Core/FormatEntity.h"
+#include "lldb/Utility/Cloneable.h"
#include "lldb/Utility/CompletionRequest.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Status.h"
virtual void Clear() = 0;
- virtual lldb::OptionValueSP DeepCopy() const = 0;
+ virtual lldb::OptionValueSP
+ DeepCopy(const lldb::OptionValueSP &new_parent) const;
virtual void AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request);
m_parent_wp = parent_sp;
}
+ lldb::OptionValueSP GetParent() const { return m_parent_wp.lock(); }
+
void SetValueChangedCallback(std::function<void()> callback) {
assert(!m_callback);
m_callback = std::move(callback);
}
protected:
+ using TopmostBase = OptionValue;
+
+ // Must be overriden by a derived class for correct downcasting the result of
+ // DeepCopy to it. Inherit from Cloneable to avoid doing this manually.
+ virtual lldb::OptionValueSP Clone() const = 0;
+
lldb::OptionValueWP m_parent_wp;
std::function<void()> m_callback;
bool m_value_was_set; // This can be used to see if a value has been set
namespace lldb_private {
-class OptionValueArch : public OptionValue {
+class OptionValueArch : public Cloneable<OptionValueArch, OptionValue> {
public:
OptionValueArch() = default;
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
void AutoComplete(CommandInterpreter &interpreter,
lldb_private::CompletionRequest &request) override;
namespace lldb_private {
-class OptionValueArgs : public OptionValueArray {
+class OptionValueArgs : public Cloneable<OptionValueArgs, OptionValueArray> {
public:
OptionValueArgs()
- : OptionValueArray(
- OptionValue::ConvertTypeToMask(OptionValue::eTypeString)) {}
+ : Cloneable(OptionValue::ConvertTypeToMask(OptionValue::eTypeString)) {}
~OptionValueArgs() override = default;
namespace lldb_private {
-class OptionValueArray : public OptionValue {
+class OptionValueArray : public Cloneable<OptionValueArray, OptionValue> {
public:
OptionValueArray(uint32_t type_mask = UINT32_MAX, bool raw_value_dump = false)
: m_type_mask(type_mask), m_values(), m_raw_value_dump(raw_value_dump) {}
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
+ lldb::OptionValueSP
+ DeepCopy(const lldb::OptionValueSP &new_parent) const override;
bool IsAggregateValue() const override { return true; }
namespace lldb_private {
-class OptionValueBoolean : public OptionValue {
+class OptionValueBoolean : public Cloneable<OptionValueBoolean, OptionValue> {
public:
OptionValueBoolean(bool value)
: m_current_value(value), m_default_value(value) {}
void SetDefaultValue(bool value) { m_default_value = value; }
- lldb::OptionValueSP DeepCopy() const override;
-
protected:
bool m_current_value;
bool m_default_value;
namespace lldb_private {
-class OptionValueChar : public OptionValue {
+class OptionValueChar : public Cloneable<OptionValueChar, OptionValue> {
public:
OptionValueChar(char value)
: m_current_value(value), m_default_value(value) {}
void SetDefaultValue(char value) { m_default_value = value; }
- lldb::OptionValueSP DeepCopy() const override;
-
protected:
char m_current_value;
char m_default_value;
namespace lldb_private {
-class OptionValueDictionary : public OptionValue {
+class OptionValueDictionary
+ : public Cloneable<OptionValueDictionary, OptionValue> {
public:
OptionValueDictionary(uint32_t type_mask = UINT32_MAX,
bool raw_value_dump = true)
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
+ lldb::OptionValueSP
+ DeepCopy(const lldb::OptionValueSP &new_parent) const override;
bool IsAggregateValue() const override { return true; }
namespace lldb_private {
-class OptionValueEnumeration : public OptionValue {
+class OptionValueEnumeration
+ : public Cloneable<OptionValueEnumeration, OptionValue> {
public:
typedef int64_t enum_type;
struct EnumeratorInfo {
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
void AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) override;
namespace lldb_private {
-class OptionValueFileColonLine : public OptionValue {
+class OptionValueFileColonLine :
+ public Cloneable<OptionValueFileColonLine, OptionValue> {
public:
OptionValueFileColonLine();
OptionValueFileColonLine(const llvm::StringRef input);
m_column_number = LLDB_INVALID_COLUMN_NUMBER;
}
- lldb::OptionValueSP DeepCopy() const override;
-
void AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) override;
namespace lldb_private {
-class OptionValueFileSpec : public OptionValue {
+class OptionValueFileSpec : public Cloneable<OptionValueFileSpec, OptionValue> {
public:
OptionValueFileSpec(bool resolve = true);
m_data_mod_time = llvm::sys::TimePoint<>();
}
- lldb::OptionValueSP DeepCopy() const override;
-
void AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) override;
namespace lldb_private {
-class OptionValueFileSpecList : public OptionValue {
+class OptionValueFileSpecList
+ : public Cloneable<OptionValueFileSpecList, OptionValue> {
public:
OptionValueFileSpecList() = default;
- OptionValueFileSpecList(const FileSpecList ¤t_value)
- : m_current_value(current_value) {}
+ OptionValueFileSpecList(const OptionValueFileSpecList &other)
+ : Cloneable(other), m_current_value(other.GetCurrentValue()) {}
~OptionValueFileSpecList() override = default;
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
bool IsAggregateValue() const override { return true; }
// Subclass specific functions
}
protected:
+ lldb::OptionValueSP Clone() const override;
+
mutable std::recursive_mutex m_mutex;
FileSpecList m_current_value;
};
namespace lldb_private {
-class OptionValueFormat : public OptionValue {
+class OptionValueFormat
+ : public Cloneable<OptionValueFormat, OptionValue> {
public:
OptionValueFormat(lldb::Format value)
: m_current_value(value), m_default_value(value) {}
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
lldb::Format GetCurrentValue() const { return m_current_value; }
namespace lldb_private {
-class OptionValueFormatEntity : public OptionValue {
+class OptionValueFormatEntity
+ : public Cloneable<OptionValueFormatEntity, OptionValue> {
public:
OptionValueFormatEntity(const char *default_format);
void Clear() override;
- lldb::OptionValueSP DeepCopy() const override;
-
void AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) override;
namespace lldb_private {
-class OptionValueLanguage : public OptionValue {
+class OptionValueLanguage : public Cloneable<OptionValueLanguage, OptionValue> {
public:
OptionValueLanguage(lldb::LanguageType value)
: m_current_value(value), m_default_value(value) {}
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
lldb::LanguageType GetCurrentValue() const { return m_current_value; }
namespace lldb_private {
-class OptionValuePathMappings : public OptionValue {
+class OptionValuePathMappings
+ : public Cloneable<OptionValuePathMappings, OptionValue> {
public:
OptionValuePathMappings(bool notify_changes)
: m_notify_changes(notify_changes) {}
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
bool IsAggregateValue() const override { return true; }
// Subclass specific functions
#include "lldb/Utility/ConstString.h"
namespace lldb_private {
+class Properties;
class OptionValueProperties
- : public OptionValue,
+ : public Cloneable<OptionValueProperties, OptionValue>,
public std::enable_shared_from_this<OptionValueProperties> {
public:
OptionValueProperties() = default;
OptionValueProperties(ConstString name);
- OptionValueProperties(const OptionValueProperties &global_properties);
-
~OptionValueProperties() override = default;
Type GetType() const override { return eTypeProperties; }
void Clear() override;
- lldb::OptionValueSP DeepCopy() const override;
+ static lldb::OptionValuePropertiesSP
+ CreateLocalCopy(const Properties &global_properties);
+
+ lldb::OptionValueSP
+ DeepCopy(const lldb::OptionValueSP &new_parent) const override;
Status
SetValueFromString(llvm::StringRef value,
namespace lldb_private {
-class OptionValueRegex : public OptionValue {
+class OptionValueRegex : public Cloneable<OptionValueRegex, OptionValue> {
public:
OptionValueRegex(const char *value = nullptr)
: m_regex(llvm::StringRef::withNullAsEmpty(value)),
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
const RegularExpression *GetCurrentValue() const {
return (m_regex.IsValid() ? &m_regex : nullptr);
namespace lldb_private {
-class OptionValueSInt64 : public OptionValue {
+class OptionValueSInt64 : public Cloneable<OptionValueSInt64, OptionValue> {
public:
OptionValueSInt64() = default;
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
const int64_t &operator=(int64_t value) {
namespace lldb_private {
-class OptionValueString : public OptionValue {
+class OptionValueString : public Cloneable<OptionValueString, OptionValue> {
public:
typedef Status (*ValidatorCallback)(const char *string, void *baton);
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
Flags &GetOptions() { return m_options; }
namespace lldb_private {
-class OptionValueUInt64 : public OptionValue {
+class OptionValueUInt64 : public Cloneable<OptionValueUInt64, OptionValue> {
public:
OptionValueUInt64() = default;
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
const uint64_t &operator=(uint64_t value) {
namespace lldb_private {
-class OptionValueUUID : public OptionValue {
+class OptionValueUUID : public Cloneable<OptionValueUUID, OptionValue> {
public:
OptionValueUUID() = default;
m_value_was_set = false;
}
- lldb::OptionValueSP DeepCopy() const override;
-
// Subclass specific functions
UUID &GetCurrentValue() { return m_uuid; }
--- /dev/null
+//===-- Cloneable.h ---------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_CLONEABLE_H
+#define LLDB_UTILITY_CLONEABLE_H
+
+#include <type_traits>
+
+namespace lldb_private {
+
+/// \class Cloneable Cloneable.h "lldb/Utility/Cloneable.h"
+/// A class that implements CRTP-based "virtual constructor" idiom.
+///
+/// Example:
+/// @code
+/// class Base {
+/// using TopmostBase = Base;
+/// public:
+/// virtual std::shared_ptr<Base> Clone() const = 0;
+/// };
+/// @endcode
+///
+/// To define a class derived from the Base with overridden Clone:
+/// @code
+/// class Intermediate : public Cloneable<Intermediate, Base> {};
+/// @endcode
+///
+/// To define a class at the next level of inheritance with overridden Clone:
+/// @code
+/// class Derived : public Cloneable<Derived, Intermediate> {};
+/// @endcode
+
+template <typename Derived, typename Base>
+class Cloneable : public Base {
+public:
+ using Base::Base;
+
+ std::shared_ptr<typename Base::TopmostBase> Clone() const override {
+ // std::is_base_of requires derived type to be complete, that's why class
+ // scope static_assert cannot be used.
+ static_assert(std::is_base_of<Cloneable, Derived>::value,
+ "Derived class must be derived from this.");
+
+ return std::make_shared<Derived>(static_cast<const Derived &>(*this));
+ }
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_CLONEABLE_H
return dumped_something;
}
+OptionValueSP OptionValue::DeepCopy(const OptionValueSP &new_parent) const {
+ auto &&clone = Clone();
+ clone->SetParent(new_parent);
+ return clone;
+}
+
void OptionValue::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {}
return error;
}
-lldb::OptionValueSP OptionValueArch::DeepCopy() const {
- return OptionValueSP(new OptionValueArch(*this));
-}
-
void OptionValueArch::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
CommandCompletions::InvokeCommonCompletionCallbacks(
return error;
}
-lldb::OptionValueSP OptionValueArray::DeepCopy() const {
- OptionValueArray *copied_array =
- new OptionValueArray(m_type_mask, m_raw_value_dump);
- lldb::OptionValueSP copied_value_sp(copied_array);
- *static_cast<OptionValue *>(copied_array) = *this;
- copied_array->m_callback = m_callback;
- const uint32_t size = m_values.size();
- for (uint32_t i = 0; i < size; ++i) {
- copied_array->AppendValue(m_values[i]->DeepCopy());
- }
- return copied_value_sp;
+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;
}
return error;
}
-lldb::OptionValueSP OptionValueBoolean::DeepCopy() const {
- return OptionValueSP(new OptionValueBoolean(*this));
-}
-
void OptionValueBoolean::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
llvm::StringRef autocomplete_entries[] = {"true", "false", "on", "off",
}
return error;
}
-
-lldb::OptionValueSP OptionValueChar::DeepCopy() const {
- return OptionValueSP(new OptionValueChar(*this));
-}
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;
+OptionValueSP
+OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const {
+ auto copy_sp = OptionValue::DeepCopy(new_parent);
+ // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived
+ // types that override GetType returning a different value.
+ auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get());
+ lldbassert(dict_value_ptr);
+
+ for (auto &value : dict_value_ptr->m_values)
+ value.second = value.second->DeepCopy(copy_sp);
+
+ return copy_sp;
}
m_enumerations.Sort();
}
-lldb::OptionValueSP OptionValueEnumeration::DeepCopy() const {
- return OptionValueSP(new OptionValueEnumeration(*this));
-}
-
void OptionValueEnumeration::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
const uint32_t num_enumerators = m_enumerations.GetSize();
return error;
}
-lldb::OptionValueSP OptionValueFileColonLine::DeepCopy() const {
- return OptionValueSP(new OptionValueFileColonLine(*this));
-}
-
void OptionValueFileColonLine::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
CommandCompletions::InvokeCommonCompletionCallbacks(
return error;
}
-lldb::OptionValueSP OptionValueFileSpec::DeepCopy() const {
- return OptionValueSP(new OptionValueFileSpec(*this));
-}
-
void OptionValueFileSpec::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
CommandCompletions::InvokeCommonCompletionCallbacks(
return error;
}
-lldb::OptionValueSP OptionValueFileSpecList::DeepCopy() const {
+OptionValueSP OptionValueFileSpecList::Clone() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
- return OptionValueSP(new OptionValueFileSpecList(m_current_value));
+ return Cloneable::Clone();
}
}
return error;
}
-
-lldb::OptionValueSP OptionValueFormat::DeepCopy() const {
- return OptionValueSP(new OptionValueFormat(*this));
-}
return error;
}
-lldb::OptionValueSP OptionValueFormatEntity::DeepCopy() const {
- return OptionValueSP(new OptionValueFormatEntity(*this));
-}
-
void OptionValueFormatEntity::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
FormatEntity::AutoComplete(request);
}
return error;
}
-
-lldb::OptionValueSP OptionValueLanguage::DeepCopy() const {
- return OptionValueSP(new OptionValueLanguage(*this));
-}
}
return error;
}
-
-lldb::OptionValueSP OptionValuePathMappings::DeepCopy() const {
- return OptionValueSP(new OptionValuePathMappings(*this));
-}
OptionValueProperties::OptionValueProperties(ConstString name) : m_name(name) {}
-OptionValueProperties::OptionValueProperties(
- const OptionValueProperties &global_properties)
- : OptionValue(global_properties),
- m_name(global_properties.m_name),
- m_properties(global_properties.m_properties),
- m_name_to_index(global_properties.m_name_to_index) {
- // We now have an exact copy of "global_properties". We need to now find all
- // non-global settings and copy the property values so that all non-global
- // settings get new OptionValue instances created for them.
- const size_t num_properties = m_properties.size();
- for (size_t i = 0; i < num_properties; ++i) {
- // Duplicate any values that are not global when constructing properties
- // from a global copy
- if (!m_properties[i].IsGlobal()) {
- lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy());
- m_properties[i].SetOptionValue(new_value_sp);
- }
- }
-}
-
size_t OptionValueProperties::GetNumProperties() const {
return m_properties.size();
}
return error;
}
-lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
- llvm_unreachable("this shouldn't happen");
+OptionValuePropertiesSP
+OptionValueProperties::CreateLocalCopy(const Properties &global_properties) {
+ auto global_props_sp = global_properties.GetValueProperties();
+ lldbassert(global_props_sp);
+
+ auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent());
+ return std::static_pointer_cast<OptionValueProperties>(copy_sp);
+}
+
+OptionValueSP
+OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const {
+ auto copy_sp = OptionValue::DeepCopy(new_parent);
+ // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived
+ // types that override GetType returning a different value.
+ auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get());
+ lldbassert(props_value_ptr);
+
+ for (auto &property : props_value_ptr->m_properties) {
+ // Duplicate any values that are not global when constructing properties
+ // from a global copy.
+ if (!property.IsGlobal()) {
+ auto value_sp = property.GetValue()->DeepCopy(copy_sp);
+ property.SetOptionValue(value_sp);
+ }
+ }
+ return copy_sp;
}
const Property *OptionValueProperties::GetPropertyAtPath(
}
return error;
}
-
-lldb::OptionValueSP OptionValueRegex::DeepCopy() const {
- return OptionValueSP(new OptionValueRegex(m_regex.GetText().str().c_str()));
-}
}
return error;
}
-
-lldb::OptionValueSP OptionValueSInt64::DeepCopy() const {
- return OptionValueSP(new OptionValueSInt64(*this));
-}
return error;
}
-lldb::OptionValueSP OptionValueString::DeepCopy() const {
- return OptionValueSP(new OptionValueString(*this));
-}
-
Status OptionValueString::SetCurrentValue(llvm::StringRef value) {
if (m_validator) {
Status error(m_validator(value.str().c_str(), m_validator_baton));
}
return error;
}
-
-lldb::OptionValueSP OptionValueUInt64::DeepCopy() const {
- return OptionValueSP(new OptionValueUInt64(*this));
-}
return error;
}
-lldb::OptionValueSP OptionValueUUID::DeepCopy() const {
- return OptionValueSP(new OptionValueUUID(*this));
-}
-
void OptionValueUUID::AutoComplete(CommandInterpreter &interpreter,
CompletionRequest &request) {
ExecutionContext exe_ctx(interpreter.GetExecutionContext());
#define DISABLE_MEM_CACHE_DEFAULT true
#endif
-class ProcessOptionValueProperties : public OptionValueProperties {
+class ProcessOptionValueProperties
+ : public Cloneable<ProcessOptionValueProperties, OptionValueProperties> {
public:
- ProcessOptionValueProperties(ConstString name)
- : OptionValueProperties(name) {}
-
- // This constructor is used when creating ProcessOptionValueProperties when
- // it is part of a new lldb_private::Process instance. It will copy all
- // current global property values as needed
- ProcessOptionValueProperties(ProcessProperties *global_properties)
- : OptionValueProperties(*global_properties->GetValueProperties()) {}
+ ProcessOptionValueProperties(ConstString name) : Cloneable(name) {}
const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
bool will_modify,
#include "TargetPropertiesEnum.inc"
};
-class ProcessExperimentalOptionValueProperties : public OptionValueProperties {
+class ProcessExperimentalOptionValueProperties
+ : public Cloneable<ProcessExperimentalOptionValueProperties,
+ OptionValueProperties> {
public:
ProcessExperimentalOptionValueProperties()
- : OptionValueProperties(
+ : Cloneable(
ConstString(Properties::GetExperimentalSettingsName())) {}
};
ConstString("thread"), ConstString("Settings specific to threads."),
true, Thread::GetGlobalProperties()->GetValueProperties());
} else {
- m_collection_sp = std::make_shared<ProcessOptionValueProperties>(
- Process::GetGlobalProperties().get());
+ m_collection_sp =
+ OptionValueProperties::CreateLocalCopy(*Process::GetGlobalProperties());
m_collection_sp->SetValueChangedCallback(
ePropertyPythonOSPluginPath,
[this] { m_process->LoadOperatingSystemPlugin(true); });
ePropertyExperimental,
};
-class TargetOptionValueProperties : public OptionValueProperties {
+class TargetOptionValueProperties
+ : public Cloneable<TargetOptionValueProperties, OptionValueProperties> {
public:
- TargetOptionValueProperties(ConstString name) : OptionValueProperties(name) {}
-
- // This constructor is used when creating TargetOptionValueProperties when it
- // is part of a new lldb_private::Target instance. It will copy all current
- // global property values as needed
- TargetOptionValueProperties(const TargetPropertiesSP &target_properties_sp)
- : OptionValueProperties(*target_properties_sp->GetValueProperties()) {}
+ TargetOptionValueProperties(ConstString name) : Cloneable(name) {}
const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
bool will_modify,
#include "TargetPropertiesEnum.inc"
};
-class TargetExperimentalOptionValueProperties : public OptionValueProperties {
+class TargetExperimentalOptionValueProperties
+ : public Cloneable<TargetExperimentalOptionValueProperties,
+ OptionValueProperties> {
public:
TargetExperimentalOptionValueProperties()
- : OptionValueProperties(
- ConstString(Properties::GetExperimentalSettingsName())) {}
+ : Cloneable(ConstString(Properties::GetExperimentalSettingsName())) {}
};
TargetExperimentalProperties::TargetExperimentalProperties()
TargetProperties::TargetProperties(Target *target)
: Properties(), m_launch_info(), m_target(target) {
if (target) {
- m_collection_sp = std::make_shared<TargetOptionValueProperties>(
- Target::GetGlobalProperties());
+ m_collection_sp =
+ OptionValueProperties::CreateLocalCopy(*Target::GetGlobalProperties());
// Set callbacks to update launch_info whenever "settins set" updated any
// of these properties
#include "TargetPropertiesEnum.inc"
};
-class ThreadOptionValueProperties : public OptionValueProperties {
+class ThreadOptionValueProperties
+ : public Cloneable<ThreadOptionValueProperties, OptionValueProperties> {
public:
- ThreadOptionValueProperties(ConstString name)
- : OptionValueProperties(name) {}
-
- // This constructor is used when creating ThreadOptionValueProperties when it
- // is part of a new lldb_private::Thread instance. It will copy all current
- // global property values as needed
- ThreadOptionValueProperties(ThreadProperties *global_properties)
- : OptionValueProperties(*global_properties->GetValueProperties()) {}
+ ThreadOptionValueProperties(ConstString name) : Cloneable(name) {}
const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
bool will_modify,
std::make_shared<ThreadOptionValueProperties>(ConstString("thread"));
m_collection_sp->Initialize(g_thread_properties);
} else
- m_collection_sp = std::make_shared<ThreadOptionValueProperties>(
- Thread::GetGlobalProperties().get());
+ m_collection_sp =
+ OptionValueProperties::CreateLocalCopy(*Thread::GetGlobalProperties());
}
ThreadProperties::~ThreadProperties() = default;
self.assertTrue(found_env_var,
"MY_ENV_VAR was not set in LunchInfo object")
+ self.assertEqual(launch_info.GetNumArguments(), 3)
+ self.assertEqual(launch_info.GetArgumentAtIndex(0), "A")
+ self.assertEqual(launch_info.GetArgumentAtIndex(1), "B")
+ self.assertEqual(launch_info.GetArgumentAtIndex(2), "C")
+
self.expect(
'target show-launch-environment',
substrs=["MY_ENV_VAR=YES"])
add_lldb_unittest(InterpreterTests
TestCompletion.cpp
TestOptionArgParser.cpp
+ TestOptionValue.cpp
TestOptionValueFileColonLine.cpp
LINK_LIBS
--- /dev/null
+//===-- TestOptionValue.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/OptionValues.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+class Callback {
+public:
+ virtual void Invoke() const {}
+ void operator()() const { Invoke(); }
+};
+
+class MockCallback : public Callback {
+public:
+ MOCK_CONST_METHOD0(Invoke, void());
+};
+
+// Test a single-value class.
+TEST(OptionValueString, DeepCopy) {
+ OptionValueString str;
+ str.SetValueFromString("ab");
+
+ MockCallback callback;
+ str.SetValueChangedCallback([&callback] { callback(); });
+ EXPECT_CALL(callback, Invoke());
+
+ auto copy_sp = str.DeepCopy(nullptr);
+
+ // Test that the base class data members are copied/set correctly.
+ ASSERT_TRUE(copy_sp);
+ ASSERT_EQ(copy_sp->GetParent().get(), nullptr);
+ ASSERT_TRUE(copy_sp->OptionWasSet());
+ ASSERT_EQ(copy_sp->GetStringValue(), "ab");
+
+ // Trigger the callback.
+ copy_sp->SetValueFromString("c", eVarSetOperationAppend);
+ ASSERT_EQ(copy_sp->GetStringValue(), "abc");
+}
+
+// Test an aggregate class.
+TEST(OptionValueArgs, DeepCopy) {
+ OptionValueArgs args;
+ args.SetValueFromString("A B");
+
+ MockCallback callback;
+ args.SetValueChangedCallback([&callback] { callback(); });
+ EXPECT_CALL(callback, Invoke());
+
+ auto copy_sp = args.DeepCopy(nullptr);
+
+ // Test that the base class data members are copied/set correctly.
+ ASSERT_TRUE(copy_sp);
+ ASSERT_EQ(copy_sp->GetParent(), nullptr);
+ ASSERT_TRUE(copy_sp->OptionWasSet());
+
+ auto *args_copy_ptr = copy_sp->GetAsArgs();
+ ASSERT_EQ(args_copy_ptr->GetSize(), 2U);
+ ASSERT_EQ((*args_copy_ptr)[0]->GetParent(), copy_sp);
+ ASSERT_EQ((*args_copy_ptr)[0]->GetStringValue(), "A");
+ ASSERT_EQ((*args_copy_ptr)[1]->GetParent(), copy_sp);
+ ASSERT_EQ((*args_copy_ptr)[1]->GetStringValue(), "B");
+
+ // Trigger the callback.
+ copy_sp->SetValueFromString("C", eVarSetOperationAppend);
+ ASSERT_TRUE(args_copy_ptr);
+ ASSERT_EQ(args_copy_ptr->GetSize(), 3U);
+ ASSERT_EQ((*args_copy_ptr)[2]->GetStringValue(), "C");
+}
+
+class TestProperties : public OptionValueProperties {
+public:
+ static std::shared_ptr<TestProperties> CreateGlobal() {
+ auto props_sp = std::make_shared<TestProperties>();
+ const bool is_global = false;
+
+ auto dict_sp = std::make_shared<OptionValueDictionary>(1 << eTypeUInt64);
+ props_sp->AppendProperty(ConstString("dict"), ConstString(), is_global,
+ dict_sp);
+
+ auto file_list_sp = std::make_shared<OptionValueFileSpecList>();
+ props_sp->AppendProperty(ConstString("file-list"), ConstString(), is_global,
+ file_list_sp);
+ return props_sp;
+ }
+
+ void SetDictionaryChangedCallback(const MockCallback &callback) {
+ SetValueChangedCallback(m_dict_index, [&callback] { callback(); });
+ }
+
+ void SetFileListChangedCallback(const MockCallback &callback) {
+ SetValueChangedCallback(m_file_list_index, [&callback] { callback(); });
+ }
+
+ OptionValueDictionary *GetDictionary() {
+ return GetPropertyAtIndexAsOptionValueDictionary(nullptr, m_dict_index);
+ }
+
+ OptionValueFileSpecList *GetFileList() {
+ return GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, true,
+ m_file_list_index);
+ }
+
+private:
+ lldb::OptionValueSP Clone() const {
+ return std::make_shared<TestProperties>(*this);
+ }
+
+ uint32_t m_dict_index = 0;
+ uint32_t m_file_list_index = 1;
+};
+
+// Test a user-defined propery class.
+TEST(TestProperties, DeepCopy) {
+ auto props_sp = TestProperties::CreateGlobal();
+ props_sp->GetDictionary()->SetValueFromString("A=1 B=2");
+ props_sp->GetFileList()->SetValueFromString("path/to/file");
+
+ MockCallback callback;
+ props_sp->SetDictionaryChangedCallback(callback);
+ props_sp->SetFileListChangedCallback(callback);
+ EXPECT_CALL(callback, Invoke()).Times(2);
+
+ auto copy_sp = props_sp->DeepCopy(nullptr);
+
+ // Test that the base class data members are copied/set correctly.
+ ASSERT_TRUE(copy_sp);
+ ASSERT_EQ(copy_sp->GetParent(), nullptr);
+
+ // This cast is safe only if the class overrides Clone().
+ auto *props_copy_ptr = static_cast<TestProperties *>(copy_sp.get());
+ ASSERT_TRUE(props_copy_ptr);
+
+ // Test the first child.
+ auto dict_copy_ptr = props_copy_ptr->GetDictionary();
+ ASSERT_TRUE(dict_copy_ptr);
+ ASSERT_EQ(dict_copy_ptr->GetParent(), copy_sp);
+ ASSERT_TRUE(dict_copy_ptr->OptionWasSet());
+ ASSERT_EQ(dict_copy_ptr->GetNumValues(), 2U);
+
+ auto value_ptr = dict_copy_ptr->GetValueForKey(ConstString("A"));
+ ASSERT_TRUE(value_ptr);
+ ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr);
+ ASSERT_EQ(value_ptr->GetUInt64Value(), 1U);
+
+ value_ptr = dict_copy_ptr->GetValueForKey(ConstString("B"));
+ ASSERT_TRUE(value_ptr);
+ ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr);
+ ASSERT_EQ(value_ptr->GetUInt64Value(), 2U);
+
+ // Test the second child.
+ auto file_list_copy_ptr = props_copy_ptr->GetFileList();
+ ASSERT_TRUE(file_list_copy_ptr);
+ ASSERT_EQ(file_list_copy_ptr->GetParent(), copy_sp);
+ ASSERT_TRUE(file_list_copy_ptr->OptionWasSet());
+
+ auto file_list_copy = file_list_copy_ptr->GetCurrentValue();
+ ASSERT_EQ(file_list_copy.GetSize(), 1U);
+ ASSERT_EQ(file_list_copy.GetFileSpecAtIndex(0), FileSpec("path/to/file"));
+
+ // Trigger the callback first time.
+ dict_copy_ptr->SetValueFromString("C=3", eVarSetOperationAppend);
+
+ // Trigger the callback second time.
+ file_list_copy_ptr->SetValueFromString("0 another/path", eVarSetOperationReplace);
+}