-
Notifications
You must be signed in to change notification settings - Fork 6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* | ||
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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
#include <libyul/ASTNodeRegistry.h> | ||
|
||
#include <libyul/Exceptions.h> | ||
|
||
#include <fmt/format.h> | ||
|
||
#include <range/v3/algorithm/max.hpp> | ||
#include <range/v3/view/map.hpp> | ||
|
||
using namespace solidity::yul; | ||
|
||
ASTNodeRegistry::ASTNodeRegistry(): m_labels{"", ghostPlaceholder}, m_idToLabelMapping{0, 1} {} | ||
|
||
ASTNodeRegistry::ASTNodeRegistry(std::vector<std::string> const& _labels, std::vector<size_t> const& _idToLabelMapping) | ||
{ | ||
yulAssert(_labels.size() >= 2); | ||
yulAssert(_labels[0].empty()); | ||
yulAssert(_labels[1] == ghostPlaceholder); | ||
yulAssert(_idToLabelMapping.size() >= 2); | ||
yulAssert(_idToLabelMapping[0] == 0); | ||
yulAssert(_idToLabelMapping[1] == 1); | ||
std::vector<uint8_t> labelVisited (_labels.size(), false); | ||
for (auto const& id: _idToLabelMapping) | ||
{ | ||
yulAssert(id < _labels.size()); | ||
// it is possible to have multiple references to empty / ghost | ||
yulAssert( | ||
id < 2 || !labelVisited[id], | ||
fmt::format("NodeId {} (label \"{}\") is not unique.", id, _labels[id]) | ||
); | ||
labelVisited[id] = true; | ||
} | ||
m_labels = _labels; | ||
m_idToLabelMapping = _idToLabelMapping; | ||
} | ||
|
||
size_t ASTNodeRegistry::idToLabelIndex(NodeId const _id) const | ||
{ | ||
yulAssert(_id < m_idToLabelMapping.size()); | ||
return m_idToLabelMapping[_id]; | ||
} | ||
|
||
std::string_view ASTNodeRegistry::operator()(NodeId const _id) const | ||
{ | ||
auto const labelIndex = idToLabelIndex(_id); | ||
if (labelIndex == ghostId()) | ||
return lookupGhost(_id); | ||
return m_labels[labelIndex]; | ||
} | ||
|
||
std::optional<ASTNodeRegistry::NodeId> ASTNodeRegistry::findIdForLabel(std::string_view const _label) const { | ||
if (_label.empty()) | ||
return emptyId(); | ||
if (_label == ghostPlaceholder) | ||
return NodeId{1}; | ||
for (NodeId id = 2; id <= maximumId(); ++id) | ||
if ((*this)(id) == _label) | ||
return id; | ||
return std::nullopt; | ||
} | ||
|
||
std::string_view ASTNodeRegistry::lookupGhost(NodeId const _id) const | ||
{ | ||
yulAssert(idToLabelIndex(_id) == ghostId()); | ||
auto const [it, _] = m_ghostLabelCache.try_emplace(_id, fmt::format("GHOST[{}]", _id)); | ||
return it->second; | ||
} | ||
|
||
ASTNodeRegistryBuilder::LabelToIdMapping::LabelToIdMapping(): | ||
m_mapping{{"", 0}, {ASTNodeRegistry::ghostPlaceholder, 1}} | ||
{} | ||
|
||
std::tuple<ASTNodeRegistry::NodeId, bool> ASTNodeRegistryBuilder::LabelToIdMapping::tryInsert( | ||
std::string_view const _label, | ||
ASTNodeRegistry::NodeId const _id | ||
) | ||
{ | ||
yulAssert(_label != ASTNodeRegistry::ghostPlaceholder); | ||
auto const [it, emplaced] = m_mapping.try_emplace(std::string{_label}, _id); | ||
return std::make_tuple(it->second, emplaced); | ||
} | ||
|
||
ASTNodeRegistryBuilder::ASTNodeRegistryBuilder(): | ||
m_nextId(2) | ||
{} | ||
|
||
ASTNodeRegistryBuilder::ASTNodeRegistryBuilder(ASTNodeRegistry const& _existingRegistry) | ||
{ | ||
for (size_t i = 2; i <= _existingRegistry.maximumId(); ++i) | ||
{ | ||
auto const existingLabel = _existingRegistry(i); | ||
if (!existingLabel.empty()) | ||
{ | ||
auto const [_, inserted] = m_mapping.tryInsert(_existingRegistry(i), i); | ||
yulAssert(inserted); | ||
} | ||
} | ||
m_nextId = _existingRegistry.maximumId() + 1; | ||
} | ||
|
||
ASTNodeRegistry::NodeId ASTNodeRegistryBuilder::define(std::string_view const _label) | ||
{ | ||
auto const [id, inserted] = m_mapping.tryInsert(_label, m_nextId); | ||
if (inserted) | ||
m_nextId++; | ||
return id; | ||
} | ||
|
||
ASTNodeRegistry ASTNodeRegistryBuilder::build() const | ||
{ | ||
auto const& mapping = m_mapping.data(); | ||
yulAssert(mapping.contains("")); | ||
yulAssert(mapping.at("") == 0); | ||
yulAssert(mapping.contains(ASTNodeRegistry::ghostPlaceholder)); | ||
yulAssert(mapping.at(ASTNodeRegistry::ghostPlaceholder) == 1); | ||
|
||
std::vector<std::string> labels{"", ASTNodeRegistry::ghostPlaceholder}; | ||
labels.reserve(mapping.size()); | ||
std::vector<size_t> idToLabelMapping(ranges::max(mapping | ranges::views::values) + 1, 0); | ||
yulAssert(idToLabelMapping.size() >= 2, "Mapping at least contains empty and ghost entry"); | ||
idToLabelMapping[1] = 1; | ||
for (auto const& [label, id]: mapping) | ||
{ | ||
// skip empty and ghost | ||
if (id < 2) | ||
continue; | ||
|
||
labels.emplace_back(label); | ||
idToLabelMapping[id] = labels.size() - 1; | ||
} | ||
return ASTNodeRegistry{labels, idToLabelMapping}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
#pragma once | ||
|
||
#include <map> | ||
#include <optional> | ||
#include <string> | ||
#include <string_view> | ||
#include <vector> | ||
|
||
namespace solidity::yul | ||
{ | ||
|
||
class ASTNodeRegistry | ||
{ | ||
public: | ||
/// unsafe to use from a different registry instance, it is up to the user to safeguard against this | ||
using NodeId = size_t; | ||
|
||
static constexpr auto ghostPlaceholder = "GHOST[@]"; | ||
|
||
ASTNodeRegistry(); | ||
ASTNodeRegistry(std::vector<std::string> const& _labels, std::vector<size_t> const& _idToLabelMapping); | ||
|
||
std::string_view operator()(NodeId _id) const; | ||
|
||
static bool constexpr empty(NodeId const _id) { return _id == emptyId(); } | ||
static NodeId constexpr emptyId() { return 0; } | ||
static NodeId constexpr ghostId() { return 1; } | ||
|
||
std::vector<std::string> const& labels() const { return m_labels; } | ||
NodeId maximumId() const { return m_idToLabelMapping.size() - 1; } | ||
|
||
size_t idToLabelIndex(NodeId _id) const; | ||
/// this is a potentially expensive operation | ||
std::optional<NodeId> findIdForLabel(std::string_view _label) const; | ||
private: | ||
std::string_view lookupGhost(NodeId _id) const; | ||
|
||
std::vector<std::string> m_labels; | ||
std::vector<size_t> m_idToLabelMapping; | ||
mutable std::map<NodeId, std::string> m_ghostLabelCache; | ||
}; | ||
|
||
class ASTNodeRegistryBuilder | ||
{ | ||
public: | ||
ASTNodeRegistryBuilder(); | ||
explicit ASTNodeRegistryBuilder(ASTNodeRegistry const& _existingRegistry); | ||
ASTNodeRegistry::NodeId define(std::string_view _label); | ||
ASTNodeRegistry build() const; | ||
private: | ||
class LabelToIdMapping | ||
{ | ||
public: | ||
LabelToIdMapping(); | ||
std::tuple<ASTNodeRegistry::NodeId, bool> tryInsert(std::string_view _label, ASTNodeRegistry::NodeId _id); | ||
|
||
auto const& data() const { return m_mapping; } | ||
private: | ||
std::map<std::string, size_t, std::less<>> m_mapping; | ||
}; | ||
|
||
LabelToIdMapping m_mapping; | ||
size_t m_nextId = 0; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters