-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
5 changed files
with
329 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
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,162 @@ | ||
/* | ||
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/YulName.h> | ||
|
||
#include <libyul/Exceptions.h> | ||
|
||
#include <fmt/compile.h> | ||
|
||
namespace solidity::yul | ||
{ | ||
|
||
YulNameRepository::YulNameRepository() | ||
{ | ||
auto const emptyName = defineName(""); | ||
yulAssert(emptyName == YulNameRepository::emptyName()); | ||
} | ||
|
||
std::optional<std::string_view> YulNameRepository::labelOf(YulName _name) const | ||
{ | ||
if (!isDerivedName(_name)) | ||
{ | ||
// if the parent is directly a defined label, we take that one | ||
auto const labelIndex = std::get<0>(m_names[static_cast<size_t>(_name)]); | ||
yulAssert(labelIndex <= std::numeric_limits<size_t>::max()); | ||
return m_definedLabels.at(static_cast<size_t>(labelIndex)); | ||
} | ||
return std::nullopt; | ||
} | ||
|
||
std::string_view YulNameRepository::requiredLabelOf(YulName const& _name) const | ||
{ | ||
auto const label = labelOf(_name); | ||
yulAssert(label.has_value(), "YulName currently has no defined label in the YulNameRepository."); | ||
return label.value(); | ||
} | ||
|
||
YulNameRepository::YulName YulNameRepository::baseNameOf(YulName _name) const | ||
{ | ||
while (isDerivedName(_name)) | ||
_name = std::get<0>(m_names[static_cast<size_t>(_name)]); | ||
return _name; | ||
} | ||
|
||
std::string_view YulNameRepository::baseLabelOf(YulName const& _name) const | ||
{ | ||
auto const labelIndex = std::get<0>(m_names[static_cast<size_t>(baseNameOf(_name))]); | ||
yulAssert(labelIndex <= std::numeric_limits<size_t>::max()); | ||
return m_definedLabels[static_cast<size_t>(labelIndex)]; | ||
} | ||
|
||
std::optional<YulNameRepository::YulName> YulNameRepository::nameOfLabel(std::string_view const _label) const | ||
{ | ||
auto const it = std::find(m_definedLabels.begin(), m_definedLabels.end(), _label); | ||
if (it != m_definedLabels.end()) | ||
{ | ||
YulName labelName{static_cast<YulName>(std::distance(m_definedLabels.begin(), it))}; | ||
// mostly it'll be iota | ||
if (!isDerivedName(labelName) && std::get<0>(m_names[static_cast<size_t>(labelName)]) == labelName) | ||
return std::get<0>(m_names[static_cast<size_t>(labelName)]); | ||
// if not iota, we have to search | ||
auto itName = std::find(m_names.rbegin(), m_names.rend(), std::make_tuple(labelName, YulNameState::DEFINED)); | ||
if (itName != m_names.rend()) | ||
return std::get<0>(*itName); | ||
} | ||
return std::nullopt; | ||
} | ||
|
||
YulNameRepository::YulName YulNameRepository::defineName(std::string_view const _label) | ||
{ | ||
if (auto const name = nameOfLabel(_label); name.has_value()) | ||
return *name; | ||
|
||
m_definedLabels.emplace_back(_label); | ||
m_names.emplace_back(m_definedLabels.size() - 1, YulNameState::DEFINED); | ||
return static_cast<YulName>(m_names.size() - 1); | ||
} | ||
|
||
YulNameRepository::YulName YulNameRepository::deriveName(YulName const& _name) | ||
{ | ||
m_names.emplace_back(baseNameOf(_name), YulNameState::DERIVED); | ||
return static_cast<YulName>(m_names.size() - 1); | ||
} | ||
|
||
bool YulNameRepository::isDerivedName(YulName const& _name) const | ||
{ | ||
yulAssert(_name <= std::numeric_limits<size_t>::max()); | ||
return std::get<1>(m_names.at(static_cast<size_t>(_name))) == YulNameState::DERIVED; | ||
} | ||
|
||
void YulNameRepository::generateLabels(std::set<YulName> const& _usedNames, std::set<std::string> const& _illegal) | ||
{ | ||
std::set<std::string> used; | ||
std::set<YulName> toDerive; | ||
for (auto const name: _usedNames) | ||
{ | ||
if (!isDerivedName(name)) | ||
{ | ||
auto const label = labelOf(name); | ||
yulAssert(label.has_value()); | ||
auto const [it, emplaced] = used.emplace(*label); | ||
if (!emplaced || _illegal.count(*it) > 0) | ||
// there's been a clash ,e.g., by calling generate labels twice; | ||
// let's remove this name and derive it instead | ||
toDerive.insert(name); | ||
} | ||
else | ||
yulAssert(isDerivedName(name) || _illegal.count(std::string(*labelOf(name))) == 0); | ||
} | ||
|
||
std::vector<std::tuple<std::string, YulName>> generated; | ||
auto namesIt = _usedNames.begin(); | ||
for (size_t nameValue = 1; nameValue < m_names.size(); ++nameValue) | ||
{ | ||
auto name = static_cast<YulName>(nameValue); | ||
if (namesIt != _usedNames.end() && name == *namesIt) | ||
{ | ||
if (isDerivedName(name) || toDerive.find(name) != toDerive.end()) | ||
{ | ||
std::string const baseLabel(baseLabelOf(name)); | ||
std::string label (baseLabel); | ||
size_t bump = 1; | ||
while (used.count(label) > 0 || _illegal.count(label) > 0) | ||
label = fmt::format(FMT_COMPILE("{}_{}"), baseLabel, bump++); | ||
if (auto const existingDefinedName = nameOfLabel(label); existingDefinedName.has_value()) | ||
{ | ||
std::get<0>(m_names[static_cast<size_t>(name)]) = *existingDefinedName; | ||
std::get<1>(m_names[static_cast<size_t>(name)]) = YulNameState::DEFINED; | ||
} | ||
else | ||
generated.emplace_back(label, name); | ||
used.insert(label); | ||
} | ||
++namesIt; | ||
} | ||
} | ||
|
||
for (auto const& [label, name] : generated) | ||
{ | ||
yulAssert(_illegal.count(label) == 0); | ||
m_definedLabels.emplace_back(label); | ||
std::get<0>(m_names[static_cast<size_t>(name)]) = static_cast<YulName>(m_definedLabels.size() - 1); | ||
std::get<1>(m_names[static_cast<size_t>(name)]) = YulNameState::DEFINED; | ||
} | ||
} | ||
|
||
} |
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
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
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,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 <http://www.gnu.org/licenses/>. | ||
*/ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
#include <boost/test/unit_test.hpp> | ||
|
||
#include <libyul/Dialect.h> | ||
#include <libyul/YulName.h> | ||
#include <libyul/backends/evm/EVMDialect.h> | ||
|
||
using namespace solidity; | ||
using namespace solidity::yul; | ||
|
||
namespace solidity::yul::test | ||
{ | ||
|
||
BOOST_AUTO_TEST_SUITE(YulName) | ||
|
||
BOOST_AUTO_TEST_CASE(repository_generate_labels_for_derived_types) | ||
{ | ||
Dialect dialect{}; | ||
YulNameRepository nameRepository; | ||
auto const test1 = nameRepository.defineName("test1"); | ||
BOOST_CHECK(!nameRepository.isDerivedName(test1)); | ||
auto const test1_1 = nameRepository.deriveName(test1); | ||
BOOST_CHECK(nameRepository.isDerivedName(test1_1)); | ||
auto const test1_2 = nameRepository.deriveName(test1); | ||
BOOST_CHECK(nameRepository.isDerivedName(test1_2)); | ||
auto const test1_2_1 = nameRepository.deriveName(test1_2); | ||
BOOST_CHECK(nameRepository.isDerivedName(test1_2_1)); | ||
auto const test2 = nameRepository.defineName("test2_1"); | ||
auto const test2_1 = nameRepository.deriveName(test2); | ||
auto const test3 = nameRepository.defineName("test3"); | ||
auto const test3_1 = nameRepository.deriveName(test3); | ||
BOOST_CHECK(test1 != test1_1); | ||
BOOST_CHECK(test1 < test1_1); | ||
nameRepository.generateLabels({test1, test1_1, test1_2, test1_2_1, test2_1, test3, test3_1}, {"test1"}); | ||
|
||
// marking test1 as invalid means that all labels get bumped up by one | ||
BOOST_CHECK(nameRepository.labelOf(test1) == "test1_1"); | ||
BOOST_CHECK(nameRepository.labelOf(test1_1) == "test1_2"); | ||
BOOST_CHECK(nameRepository.labelOf(test1_2) == "test1_3"); | ||
BOOST_CHECK(nameRepository.labelOf(test1_2_1) == "test1_4"); | ||
|
||
BOOST_CHECK(nameRepository.labelOf(test2) == "test2_1"); | ||
// the label of test2 is reused as it's not in the used names when generating labels | ||
BOOST_CHECK(nameRepository.labelOf(test2_1) == "test2_1"); | ||
|
||
BOOST_CHECK(nameRepository.labelOf(test3) == "test3"); | ||
BOOST_CHECK(nameRepository.labelOf(test3_1) == "test3_1"); | ||
|
||
// derive a name from the (now labelled) test2_1 name | ||
auto const test2_1_1 = nameRepository.deriveName(test2_1); | ||
// but we have a conflict with an already defined/labelled name, expectation is that we get test2_1_2 | ||
auto const conflict = nameRepository.defineName("test2_1_1"); | ||
nameRepository.generateLabels({test1, test1_1, test1_2, test1_2_1, test2_1, test2_1_1, test3, test3_1, conflict}); | ||
// test2_1 is in the list, so produce a new name | ||
BOOST_CHECK(nameRepository.labelOf(test2_1_1) == "test2_1_2"); | ||
BOOST_CHECK(nameRepository.labelOf(conflict) == "test2_1_1"); | ||
|
||
nameRepository.generateLabels({test2, test2_1, test2_1_1, conflict}); | ||
BOOST_CHECK(nameRepository.labelOf(test2) == "test2_1"); | ||
// this label gets reassigned, as test2_1 is back in the game | ||
BOOST_CHECK(nameRepository.labelOf(test2_1) == "test2_1_3"); | ||
BOOST_CHECK(nameRepository.labelOf(test2_1_1) == "test2_1_2"); | ||
BOOST_CHECK(nameRepository.labelOf(conflict) == "test2_1_1"); | ||
|
||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() | ||
|
||
} |