/*
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
using namespace solidity::test::fuzzer;
using namespace solidity::test::fuzzer::mutator;
using namespace solidity::util;
using namespace std;
GeneratorBase::GeneratorBase(std::shared_ptr _mutator)
{
mutator = std::move(_mutator);
state = mutator->testState();
uRandDist = mutator->uniformRandomDist();
}
string GeneratorBase::visitChildren()
{
ostringstream os;
// Randomise visit order
vector> randomisedChildren;
for (auto const& child: generators)
randomisedChildren.push_back(child);
shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine);
for (auto const& child: randomisedChildren)
if (uRandDist->likely(child.second + 1))
for (unsigned i = 0; i < uRandDist->distributionOneToN(child.second); i++)
os << std::visit(GenericVisitor{
[&](auto const& _item) { return _item->generate(); }
}, child.first);
return os.str();
}
void SourceState::print(std::ostream& _os) const
{
for (auto const& import: importedSources)
_os << "Imports: " << import << std::endl;
}
set TestState::sourceUnitPaths() const
{
set keys;
boost::copy(sourceUnitState | boost::adaptors::map_keys, std::inserter(keys, keys.begin()));
return keys;
}
string TestState::randomPath(set const& _sourceUnitPaths) const
{
auto it = _sourceUnitPaths.begin();
/// Advance iterator by n where 0 <= n <= sourceUnitPaths.size() - 1
size_t increment = uRandDist->distributionOneToN(_sourceUnitPaths.size()) - 1;
solAssert(
increment >= 0 && increment < _sourceUnitPaths.size(),
"Solc custom mutator: Invalid increment"
);
advance(it, increment);
return *it;
}
string TestState::randomPath() const
{
solAssert(!empty(), "Solc custom mutator: Null test state");
return randomPath(sourceUnitPaths());
}
void TestState::print(std::ostream& _os) const
{
_os << "Printing test state" << std::endl;
for (auto const& item: sourceUnitState)
{
_os << "Source path: " << item.first << std::endl;
item.second->print(_os);
}
}
string TestState::randomNonCurrentPath() const
{
/// To obtain a source path that is not the currently visited
/// source unit itself, we require at least one other source
/// unit to be previously visited.
solAssert(size() >= 2, "Solc custom mutator: Invalid test state");
set filteredSourcePaths;
string currentPath = currentSourceUnitPath;
set sourcePaths = sourceUnitPaths();
copy_if(
sourcePaths.begin(),
sourcePaths.end(),
inserter(filteredSourcePaths, filteredSourcePaths.begin()),
[currentPath](string const& _item) {
return _item != currentPath;
}
);
return randomPath(filteredSourcePaths);
}
void TestCaseGenerator::setup()
{
addGenerators({
{mutator->generator(), s_maxSourceUnits}
});
}
string TestCaseGenerator::visit()
{
return visitChildren();
}
void SourceUnitGenerator::setup()
{
addGenerators({
{mutator->generator(), s_maxImports},
{mutator->generator(), 1}
});
}
string SourceUnitGenerator::visit()
{
state->addSource();
ostringstream os;
os << "\n"
<< "==== Source: "
<< state->currentPath()
<< " ===="
<< "\n";
os << visitChildren();
return os.str();
}
string PragmaGenerator::visit()
{
set pragmas = uRandDist->subset(s_genericPragmas);
// Choose either abicoder v1 or v2 but not both.
pragmas.insert(s_abiPragmas[uRandDist->distributionOneToN(s_abiPragmas.size()) - 1]);
return boost::algorithm::join(pragmas, "\n") + "\n";
}
string ImportGenerator::visit()
{
/*
* Case 1: No source units defined
* Case 2: One source unit defined
* Case 3: At least two source units defined
*/
ostringstream os;
string importPath;
// Import a different source unit if at least
// two source units available.
if (state->size() > 1)
importPath = state->randomNonCurrentPath();
// Do not reimport already imported source unit
if (!importPath.empty() && !state->sourceUnitState[state->currentPath()]->sourcePathImported(importPath))
{
os << "import "
<< "\""
<< importPath
<< "\";\n";
state->sourceUnitState[state->currentPath()]->addImportedSourcePath(importPath);
}
return os.str();
}
template
shared_ptr SolidityGenerator::generator()
{
for (auto& g: m_generators)
if (holds_alternative>(g))
return get>(g);
solAssert(false, "");
}
SolidityGenerator::SolidityGenerator(unsigned _seed)
{
m_generators = {};
m_urd = make_shared(make_unique(_seed));
m_state = make_shared(m_urd);
}
template
void SolidityGenerator::createGenerators()
{
if constexpr (I < std::variant_size_v)
{
createGenerator>();
createGenerators();
}
}
string SolidityGenerator::generateTestProgram()
{
createGenerators();
for (auto& g: m_generators)
std::visit(GenericVisitor{
[&](auto const& _item) { return _item->setup(); }
}, g);
string program = generator()->generate();
destroyGenerators();
return program;
}