diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index 28216207c..626d2945a 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -3,6 +3,8 @@ set(sources Common.h CharStream.cpp CharStream.h + DebugInfoSelection.cpp + DebugInfoSelection.h ErrorReporter.cpp ErrorReporter.h EVMVersion.h diff --git a/liblangutil/DebugInfoSelection.cpp b/liblangutil/DebugInfoSelection.cpp new file mode 100644 index 000000000..6ff023c01 --- /dev/null +++ b/liblangutil/DebugInfoSelection.cpp @@ -0,0 +1,157 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; + +DebugInfoSelection const DebugInfoSelection::All(bool _value) noexcept +{ + DebugInfoSelection result; + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + result.*member = _value; + return result; +} + +DebugInfoSelection const DebugInfoSelection::Only(bool DebugInfoSelection::* _member) noexcept +{ + DebugInfoSelection result{}; + result.*_member = true; + return result; +} + +optional DebugInfoSelection::fromString(string_view _input) +{ + // TODO: Make more stuff constexpr and make it a static_assert(). + solAssert(componentMap().count("all") == 0, ""); + solAssert(componentMap().count("none") == 0, ""); + + if (_input == "all") + return All(); + if (_input == "none") + return None(); + + return fromComponents(_input | ranges::views::split(',') | ranges::to>); +} + +optional DebugInfoSelection::fromComponents( + vector const& _componentNames, + bool _acceptWildcards +) +{ + solAssert(componentMap().count("*") == 0, ""); + + DebugInfoSelection selection; + for (auto const& component: _componentNames) + { + if (component == "*") + return (_acceptWildcards ? make_optional(DebugInfoSelection::All()) : nullopt); + + if (!selection.enable(component)) + return nullopt; + } + + return selection; +} + +bool DebugInfoSelection::enable(string _component) +{ + auto memberIt = componentMap().find(boost::trim_copy(_component)); + if (memberIt == componentMap().end()) + return false; + + this->*(memberIt->second) = true; + return true; +} + +bool DebugInfoSelection::any() const noexcept +{ + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + if (this->*member) + return true; + + return false; +} + +bool DebugInfoSelection::all() const noexcept +{ + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + if (!(this->*member)) + return false; + + return true; +} + +DebugInfoSelection& DebugInfoSelection::operator&=(DebugInfoSelection const& _other) +{ + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + this->*member &= _other.*member; + return *this; +} + +DebugInfoSelection& DebugInfoSelection::operator|=(DebugInfoSelection const& _other) +{ + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + this->*member |= _other.*member; + return *this; +} + +DebugInfoSelection DebugInfoSelection::operator&(DebugInfoSelection _other) const noexcept +{ + _other &= *this; + return _other; +} + +DebugInfoSelection DebugInfoSelection::operator|(DebugInfoSelection _other) const noexcept +{ + _other |= *this; + return _other; +} + +bool DebugInfoSelection::operator==(DebugInfoSelection const& _other) const noexcept +{ + for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values) + if (this->*member != _other.*member) + return false; + return true; +} + +ostream& langutil::operator<<(ostream& _stream, DebugInfoSelection const& _selection) +{ + vector selectedComponentNames; + for (auto const& [name, member]: _selection.componentMap()) + if (_selection.*member) + selectedComponentNames.push_back(name); + + return _stream << joinHumanReadable(selectedComponentNames, ","); +} diff --git a/liblangutil/DebugInfoSelection.h b/liblangutil/DebugInfoSelection.h new file mode 100644 index 000000000..40ba3d6b2 --- /dev/null +++ b/liblangutil/DebugInfoSelection.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Handles selections of debug info components. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ + +/** + * Represents a set of flags corresponding to components of debug info selected for some purpose. + * + * Provides extra functionality for enumerating the components and serializing/deserializing the + * selection to/from a comma-separated string. + */ +struct DebugInfoSelection +{ + static DebugInfoSelection const All(bool _value = true) noexcept; + static DebugInfoSelection const None() noexcept { return All(false); } + static DebugInfoSelection const Only(bool DebugInfoSelection::* _member) noexcept; + static DebugInfoSelection const Default() noexcept { return All(); } + + static std::optional fromString(std::string_view _input); + static std::optional fromComponents( + std::vector const& _componentNames, + bool _acceptWildcards = false + ); + bool enable(std::string _component); + + bool all() const noexcept; + bool any() const noexcept; + bool none() const noexcept { return !any(); } + bool only(bool DebugInfoSelection::* _member) const noexcept { return *this == Only(_member); } + + DebugInfoSelection& operator&=(DebugInfoSelection const& _other); + DebugInfoSelection& operator|=(DebugInfoSelection const& _other); + DebugInfoSelection operator&(DebugInfoSelection _other) const noexcept; + DebugInfoSelection operator|(DebugInfoSelection _other) const noexcept; + + bool operator!=(DebugInfoSelection const& _other) const noexcept { return !(*this == _other); } + bool operator==(DebugInfoSelection const& _other) const noexcept; + + friend std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection); + + static auto const& componentMap() + { + static std::map const components = { + {"location", &DebugInfoSelection::location}, + {"snippet", &DebugInfoSelection::snippet}, + {"ast-id", &DebugInfoSelection::astID}, + }; + return components; + } + + bool location = false; ///< Include source location. E.g. `@src 3:50:100` + bool snippet = false; ///< Include source code snippet next to location. E.g. `@src 3:50:100 "contract C {..."` + bool astID = false; ///< Include ID of the Solidity AST node. E.g. `@ast-id 15` +}; + +std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection); + +}