Index: lldb/include/lldb/DataFormatters/ValueObjectPrinter.h =================================================================== --- lldb/include/lldb/DataFormatters/ValueObjectPrinter.h +++ lldb/include/lldb/DataFormatters/ValueObjectPrinter.h @@ -13,7 +13,7 @@ #include "lldb/lldb-private.h" #include "lldb/lldb-public.h" -#include "lldb/Utility/Flags.h" +#include "lldb/Utility/EnumFlags.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/Symbol/CompilerType.h" @@ -122,7 +122,7 @@ ValueObject *m_valobj; Stream *m_stream; DumpValueObjectOptions m_options; - Flags m_type_flags; + EnumFlags m_type_flags; CompilerType m_compiler_type; DumpValueObjectOptions::PointerDepth m_ptr_depth; uint32_t m_curr_depth; Index: lldb/include/lldb/Host/ProcessLaunchInfo.h =================================================================== --- lldb/include/lldb/Host/ProcessLaunchInfo.h +++ lldb/include/lldb/Host/ProcessLaunchInfo.h @@ -13,7 +13,7 @@ #include // LLDB Headers -#include "lldb/Utility/Flags.h" +#include "lldb/Utility/EnumFlags.h" #include "lldb/Host/FileAction.h" #include "lldb/Host/Host.h" @@ -60,9 +60,9 @@ const FileAction *GetFileActionForFD(int fd) const; - Flags &GetFlags() { return m_flags; } + EnumFlags &GetFlags() { return m_flags; } - const Flags &GetFlags() const { return m_flags; } + const EnumFlags &GetFlags() const { return m_flags; } const FileSpec &GetWorkingDirectory() const; @@ -150,7 +150,8 @@ FileSpec m_working_dir; std::string m_plugin_name; FileSpec m_shell; - Flags m_flags; // Bitwise OR of bits from lldb::LaunchFlags + EnumFlags + m_flags; // Bitwise OR of bits from lldb::LaunchFlags std::vector m_file_actions; // File actions for any other files std::shared_ptr m_pty; uint32_t m_resume_count; // How many times do we resume after launching Index: lldb/include/lldb/Utility/EnumFlags.h =================================================================== --- /dev/null +++ lldb/include/lldb/Utility/EnumFlags.h @@ -0,0 +1,119 @@ +//===-- EnumFlags.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_ENUMFLAGS_H +#define LLDB_UTILITY_ENUMFLAGS_H + +#include "llvm/ADT/ArrayRef.h" +#include +#include +#include + +namespace lldb_private { + +/// \class EnumFlags EnumFlags.h "lldb/Utility/EnumFlags.h" +/// Typed wrapper for enum-based bit flags. +/// +/// EnumFlags provides functions for reading and writing specific bits in the +/// underlying flags storage. If possible, all functions are typed with the +/// actual enum that is used to describe the meaning of the different bits. +/// +/// This class supports both scoped and unscoped enums. +template class EnumFlags { + /// The integer type that can store all the bits that can be described by + /// the enumerators of this specialization. + typedef std::underlying_type_t ValueType; + /// Utility function for converting the enum type to the used ValueType. + static ValueType ToValueType(T e) { return static_cast(e); } + + /// Bitwise ORs all passed enumerators into a integer compatible with the + /// internal integer storage for the flags. + static ValueType MergeEnumArgs(llvm::ArrayRef flags) { + ValueType Merged = 0; + for (T e : flags) + Merged |= ToValueType(e); + return Merged; + } + +public: + EnumFlags() = default; + explicit EnumFlags(llvm::ArrayRef flags) { Set(flags); } + + /// Clears all flags. + void Clear() { m_flags = 0; } + + /// Clears the bits for the given enumerator. + void Clear(T e) { m_flags &= ~ToValueType(e); } + + /// Clears all bits described by any of the given enumerators. + void Clear(llvm::ArrayRef flags) { m_flags &= ~MergeEnumArgs(flags); } + + /// Sets the bits for the given enumerator. + void Set(T e) { m_flags |= ToValueType(e); } + + /// Sets all bits described by any of the given enumerators. + void Set(llvm::ArrayRef flags) { m_flags |= MergeEnumArgs(flags); } + + /// Tests if all bits described by the given enumerator are set. + /// \return True iff the respective bits are set. + bool Test(T e) const { return (m_flags & ToValueType(e)) != 0; } + + /// Tests if all bits described by the given enumerator are not set. + /// \return True iff the respective bits are not set. + bool IsClear(T e) const { return !Test(e); } + + /// Returns true iff every bit that belongs to one of the passed enumerators + /// is set. + bool AllSet(llvm::ArrayRef flags) const { + ValueType mask = MergeEnumArgs(flags); + return (m_flags & mask) == mask; + } + + /// Returns true iff at least one bit that belongs to one of the passed + /// enumerators is set. + bool AnySet(llvm::ArrayRef flags) const { + return m_flags & MergeEnumArgs(flags); + } + + /// Returns true iff every bit that belongs to one of the passed + /// enumerators is set. + bool AllClear(llvm::ArrayRef flags) const { + return (m_flags & MergeEnumArgs(flags)) == 0; + } + + /// Returns true iff at least one bit that belongs to one of the passed + /// enumerators is not set. + bool AnyClear(llvm::ArrayRef flags) const { + ValueType mask = MergeEnumArgs(flags); + return (m_flags & mask) != mask; + } + + /// Returns the internal integer storage that is used to store the flag bits. + /// All bits of the set enumerators are just bitwise or'd into this storage + /// integer. + /// \note This function is not using the enum type and can't do any type + /// checking. It only exists to implement legacy interfaces from the SB API + /// and should not be used anywhere else. + ValueType GetRawEncoding() const { return m_flags; } + + /// Sets the internal integer storage that is used to store the flag bits + /// to the given value. The passed value should be bitwise or'd enumerators + /// of the respective enum of this EnumFlags specialization. + /// \note This function is not using the enum type and can't do any type + /// checking. It only exists to implement legacy interfaces from the SB API + /// and should not be used anywhere else. + void SetFromRawEncoding(ValueType t) { m_flags = t; } + +private: + /// The integer storage in which the actual + ValueType m_flags = 0; +}; + +} // namespace lldb_private + +#endif // LLDB_UTILITY_ENUMFLAGS_H Index: lldb/source/API/SBLaunchInfo.cpp =================================================================== --- lldb/source/API/SBLaunchInfo.cpp +++ lldb/source/API/SBLaunchInfo.cpp @@ -39,7 +39,7 @@ : m_opaque_sp(new SBLaunchInfoImpl()) { LLDB_RECORD_CONSTRUCTOR(SBLaunchInfo, (const char **), argv); - m_opaque_sp->GetFlags().Reset(eLaunchFlagDebug | eLaunchFlagDisableASLR); + m_opaque_sp->GetFlags().Set({eLaunchFlagDebug, eLaunchFlagDisableASLR}); if (argv && argv[0]) m_opaque_sp->GetArguments().SetArguments(argv); } @@ -227,13 +227,13 @@ uint32_t SBLaunchInfo::GetLaunchFlags() { LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetLaunchFlags); - return m_opaque_sp->GetFlags().Get(); + return m_opaque_sp->GetFlags().GetRawEncoding(); } void SBLaunchInfo::SetLaunchFlags(uint32_t flags) { LLDB_RECORD_METHOD(void, SBLaunchInfo, SetLaunchFlags, (uint32_t), flags); - m_opaque_sp->GetFlags().Reset(flags); + m_opaque_sp->GetFlags().SetFromRawEncoding(flags); } const char *SBLaunchInfo::GetProcessPluginName() { Index: lldb/source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -138,7 +138,7 @@ } } m_compiler_type = m_valobj->GetCompilerType(); - m_type_flags = m_compiler_type.GetTypeInfo(); + m_type_flags.SetFromRawEncoding(m_compiler_type.GetTypeInfo()); return true; } Index: lldb/source/Host/common/ProcessLaunchInfo.cpp =================================================================== --- lldb/source/Host/common/ProcessLaunchInfo.cpp +++ lldb/source/Host/common/ProcessLaunchInfo.cpp @@ -29,20 +29,21 @@ // ProcessLaunchInfo member functions ProcessLaunchInfo::ProcessLaunchInfo() - : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0), - m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0), - m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr), - m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() {} + : ProcessInfo(), m_working_dir(), m_plugin_name(), m_file_actions(), + m_pty(new PseudoTerminal), m_resume_count(0), m_monitor_callback(nullptr), + m_monitor_callback_baton(nullptr), m_monitor_signals(false), + m_listener_sp(), m_hijack_listener_sp() {} ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_directory, uint32_t launch_flags) - : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags), - m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0), - m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr), - m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() { + : ProcessInfo(), m_working_dir(), m_plugin_name(), m_file_actions(), + m_pty(new PseudoTerminal), m_resume_count(0), m_monitor_callback(nullptr), + m_monitor_callback_baton(nullptr), m_monitor_signals(false), + m_listener_sp(), m_hijack_listener_sp() { + m_flags.SetFromRawEncoding(launch_flags); if (stdin_file_spec) { FileAction file_action; const bool read = true; Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -740,7 +740,7 @@ LLDB_LOGF(log, "ProcessGDBRemote::%s() entered", __FUNCTION__); - uint32_t launch_flags = launch_info.GetFlags().Get(); + EnumFlags launch_flags = launch_info.GetFlags(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; @@ -778,7 +778,7 @@ __FUNCTION__); } - const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + const bool disable_stdio = launch_flags.Test(eLaunchFlagDisableSTDIO); if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file or stdio is // completely disabled @@ -798,7 +798,6 @@ error = EstablishConnectionIfNeeded(launch_info); if (error.Success()) { PseudoTerminal pty; - const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp(GetTarget().GetPlatform()); if (disable_stdio) { @@ -856,8 +855,8 @@ if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); - m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); - m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); + m_gdb_comm.SetDisableASLR(launch_flags.Test(eLaunchFlagDisableASLR)); + m_gdb_comm.SetDetachOnError(launch_flags.Test(eLaunchFlagDetachOnError)); m_gdb_comm.SendLaunchArchPacket( GetTarget().GetArchitecture().GetArchitectureName()); Index: lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp =================================================================== --- lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp +++ lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp @@ -1403,7 +1403,7 @@ // If we're not debugging this launched process, there's nothing for us to do // here. - if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug)) + if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) return error; // Darwin os_log() support automatically adds debug-level and info-level Index: lldb/tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- lldb/tools/lldb-server/lldb-gdbserver.cpp +++ lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -141,8 +141,8 @@ void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, llvm::ArrayRef Arguments) { ProcessLaunchInfo info; - info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | - eLaunchFlagDisableASLR); + info.GetFlags().Set( + {eLaunchFlagStopAtEntry, eLaunchFlagDebug, eLaunchFlagDisableASLR}); info.SetArguments(Args(Arguments), true); llvm::SmallString<64> cwd; Index: lldb/unittests/Host/ProcessLaunchInfoTest.cpp =================================================================== --- lldb/unittests/Host/ProcessLaunchInfoTest.cpp +++ lldb/unittests/Host/ProcessLaunchInfoTest.cpp @@ -23,5 +23,5 @@ EXPECT_EQ(FileSpec("/stderr"), Info.GetFileActionForFD(STDERR_FILENO)->GetFileSpec()); EXPECT_EQ(FileSpec("/wd"), Info.GetWorkingDirectory()); - EXPECT_EQ(eLaunchFlagStopAtEntry, Info.GetFlags().Get()); + EXPECT_TRUE(Info.GetFlags().Test(eLaunchFlagStopAtEntry)); } Index: lldb/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/unittests/Utility/CMakeLists.txt +++ lldb/unittests/Utility/CMakeLists.txt @@ -7,6 +7,7 @@ ConstStringTest.cpp CompletionRequestTest.cpp DataExtractorTest.cpp + EnumFlagsTest.cpp EnvironmentTest.cpp EventTest.cpp FileSpecTest.cpp Index: lldb/unittests/Utility/EnumFlagsTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/EnumFlagsTest.cpp @@ -0,0 +1,199 @@ +//===-- EnumFlagsTest.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 "gtest/gtest.h" + +#include "lldb/Utility/EnumFlags.h" + +using namespace lldb_private; + +enum DummyFlags { + eFlag0 = 1 << 0, + eFlag1 = 1 << 1, + eFlag2 = 1 << 4, +}; + +TEST(EnumFlags, DefaultConstructor) { + EnumFlags flags; + EXPECT_FALSE(flags.Test(eFlag0)); + EXPECT_FALSE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, ListConstructor) { + EnumFlags flags({eFlag0, eFlag1}); + EXPECT_TRUE(flags.Test(eFlag0)); + EXPECT_TRUE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, Set) { + EnumFlags flags; + + flags.Set(eFlag0); + EXPECT_TRUE(flags.Test(eFlag0)); + EXPECT_FALSE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); + + flags.Set(eFlag1); + EXPECT_TRUE(flags.Test(eFlag0)); + EXPECT_TRUE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, SetSeveral) { + EnumFlags flags; + + flags.Set({eFlag0, eFlag1}); + EXPECT_TRUE(flags.Test(eFlag0)); + EXPECT_TRUE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); + + flags.Set({eFlag1, eFlag2}); + EXPECT_TRUE(flags.Test(eFlag0)); + EXPECT_TRUE(flags.Test(eFlag1)); + EXPECT_TRUE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, Clear) { + EnumFlags flags({eFlag0, eFlag1}); + + flags.Clear(eFlag0); + EXPECT_FALSE(flags.Test(eFlag0)); + EXPECT_TRUE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); + + flags.Clear(eFlag1); + EXPECT_FALSE(flags.Test(eFlag0)); + EXPECT_FALSE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, ClearSeveral) { + EnumFlags flags({eFlag0, eFlag1, eFlag2}); + + flags.Clear({eFlag0, eFlag1}); + EXPECT_FALSE(flags.Test(eFlag0)); + EXPECT_FALSE(flags.Test(eFlag1)); + EXPECT_TRUE(flags.Test(eFlag2)); + + flags.Clear({eFlag1, eFlag2}); + EXPECT_FALSE(flags.Test(eFlag0)); + EXPECT_FALSE(flags.Test(eFlag1)); + EXPECT_FALSE(flags.Test(eFlag2)); +} + +TEST(EnumFlags, IsClear) { + EnumFlags flags; + + flags.Set(eFlag0); + EXPECT_FALSE(flags.IsClear(eFlag0)); + EXPECT_TRUE(flags.IsClear(eFlag1)); + EXPECT_TRUE(flags.IsClear(eFlag2)); + + flags.Set(eFlag1); + EXPECT_FALSE(flags.IsClear(eFlag0)); + EXPECT_FALSE(flags.IsClear(eFlag1)); + EXPECT_TRUE(flags.IsClear(eFlag2)); +} + +TEST(EnumFlags, AllSet) { + EnumFlags flags({eFlag0, eFlag2}); + EXPECT_TRUE(flags.AllSet({eFlag0, eFlag2})); + EXPECT_TRUE(flags.AllSet({eFlag0})); + EXPECT_FALSE(flags.AllSet({eFlag1})); + EXPECT_FALSE(flags.AllSet({eFlag0, eFlag1})); + EXPECT_FALSE(flags.AllSet({eFlag2, eFlag1, eFlag0})); +} + +TEST(EnumFlags, AnySet) { + EnumFlags flags({eFlag0, eFlag2}); + EXPECT_TRUE(flags.AnySet({eFlag0, eFlag2})); + EXPECT_TRUE(flags.AnySet({eFlag0})); + EXPECT_FALSE(flags.AnySet({eFlag1})); + EXPECT_TRUE(flags.AnySet({eFlag0, eFlag1})); + EXPECT_TRUE(flags.AnySet({eFlag2, eFlag1, eFlag0})); +} + +TEST(EnumFlags, AllClear) { + EnumFlags flags({eFlag0, eFlag2}); + EXPECT_TRUE(flags.AllSet({eFlag0, eFlag2})); + EXPECT_TRUE(flags.AllSet({eFlag0})); + EXPECT_FALSE(flags.AllSet({eFlag1})); + EXPECT_FALSE(flags.AllSet({eFlag0, eFlag1})); + EXPECT_FALSE(flags.AllSet({eFlag2, eFlag1, eFlag0})); +} + +TEST(EnumFlags, AnyClear) { + EnumFlags flags({eFlag0, eFlag2}); + EXPECT_TRUE(flags.AnySet({eFlag0, eFlag2})); + EXPECT_TRUE(flags.AnySet({eFlag0})); + EXPECT_FALSE(flags.AnySet({eFlag1})); + EXPECT_TRUE(flags.AnySet({eFlag0, eFlag1})); + EXPECT_TRUE(flags.AnySet({eFlag2, eFlag1, eFlag0})); +} + +TEST(EnumFlags, GetRawEncoding) { + EnumFlags flags; + EXPECT_EQ(flags.GetRawEncoding(), 0U); + flags.Set({eFlag0, eFlag2}); + EXPECT_EQ(flags.GetRawEncoding(), static_cast(eFlag0 | eFlag2)); + flags.Set(eFlag1); + EXPECT_EQ(flags.GetRawEncoding(), + static_cast(eFlag0 | eFlag1 | eFlag2)); +} + +TEST(EnumFlags, SetFromRawEncoding) { + EnumFlags flags; + flags.SetFromRawEncoding(eFlag0 | eFlag1); + EXPECT_EQ(flags.GetRawEncoding(), static_cast(eFlag0 | eFlag1)); + flags.SetFromRawEncoding(eFlag0); + EXPECT_TRUE(flags.Test(eFlag0)); +} + +enum class Scoped { Flag0 = (1 << 0), Flag1 = (1 << 1), Flag2 = (1 << 3) }; + +TEST(EnumFlags, ScopedEnum) { + // Call all functions and test some basic functionality with a scoped enum. + EnumFlags flags; + flags.Set(Scoped::Flag0); + EXPECT_TRUE(flags.Test(Scoped::Flag0)); + EXPECT_FALSE(flags.Test(Scoped::Flag1)); + EXPECT_FALSE(flags.Test(Scoped::Flag2)); + EXPECT_TRUE(flags.IsClear(Scoped::Flag2)); + + flags.Set({Scoped::Flag1, Scoped::Flag0}); + EXPECT_TRUE(flags.Test(Scoped::Flag0)); + EXPECT_TRUE(flags.Test(Scoped::Flag1)); + EXPECT_FALSE(flags.Test(Scoped::Flag2)); + + flags.Clear(Scoped::Flag0); + EXPECT_FALSE(flags.Test(Scoped::Flag0)); + EXPECT_TRUE(flags.Test(Scoped::Flag1)); + EXPECT_FALSE(flags.Test(Scoped::Flag2)); + + flags.Clear({Scoped::Flag0, Scoped::Flag2}); + EXPECT_FALSE(flags.Test(Scoped::Flag0)); + EXPECT_TRUE(flags.Test(Scoped::Flag1)); + EXPECT_FALSE(flags.Test(Scoped::Flag2)); + + EXPECT_TRUE(flags.AllSet({Scoped::Flag1})); + EXPECT_FALSE(flags.AllSet({Scoped::Flag1, Scoped::Flag2})); + + EXPECT_TRUE(flags.AnySet({Scoped::Flag1})); + EXPECT_FALSE(flags.AnySet({Scoped::Flag0, Scoped::Flag2})); + + EXPECT_FALSE(flags.AllClear({Scoped::Flag1})); + EXPECT_TRUE(flags.AllClear({Scoped::Flag0, Scoped::Flag2})); + + EXPECT_FALSE(flags.AnyClear({Scoped::Flag1})); + EXPECT_TRUE(flags.AnyClear({Scoped::Flag0, Scoped::Flag2})); + + EnumFlags flags2({Scoped::Flag0, Scoped::Flag1}); + EXPECT_TRUE(flags2.Test(Scoped::Flag0)); +}