Merge pull request #10913 from ethereum/refactor-solgen

Solidity fuzzer: Add test state.
This commit is contained in:
Bhargava Shastry 2021-02-09 14:18:14 +01:00 committed by GitHub
commit 4ff0069f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 38 deletions

View File

@ -24,13 +24,12 @@
using namespace solidity::test::fuzzer; using namespace solidity::test::fuzzer;
using namespace solidity::util; using namespace solidity::util;
using namespace std; using namespace std;
using PrngUtil = solidity::test::fuzzer::GenerationProbability;
GeneratorBase::GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator) GeneratorBase::GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator)
{ {
mutator = std::move(_mutator); mutator = std::move(_mutator);
rand = mutator->randomEngine();
state = mutator->testState(); state = mutator->testState();
uRandDist = mutator->uniformRandomDist();
} }
string GeneratorBase::visitChildren() string GeneratorBase::visitChildren()
@ -38,9 +37,9 @@ string GeneratorBase::visitChildren()
ostringstream os; ostringstream os;
// Randomise visit order // Randomise visit order
vector<GeneratorPtr> randomisedChildren; vector<GeneratorPtr> randomisedChildren;
for (auto child: generators) for (auto const& child: generators)
randomisedChildren.push_back(child); randomisedChildren.push_back(child);
shuffle(randomisedChildren.begin(), randomisedChildren.end(), *rand); shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine);
for (auto child: randomisedChildren) for (auto child: randomisedChildren)
os << std::visit(GenericVisitor{ os << std::visit(GenericVisitor{
[&](auto const& _item) { return _item->generate(); } [&](auto const& _item) { return _item->generate(); }
@ -52,7 +51,7 @@ string TestState::randomPath(set<string> const& _sourceUnitPaths) const
{ {
auto it = _sourceUnitPaths.begin(); auto it = _sourceUnitPaths.begin();
/// Advance iterator by n where 0 <= n <= sourceUnitPaths.size() - 1 /// Advance iterator by n where 0 <= n <= sourceUnitPaths.size() - 1
size_t increment = PrngUtil{}.distributionOneToN(_sourceUnitPaths.size(), rand) - 1; size_t increment = uRandDist->distributionOneToN(_sourceUnitPaths.size()) - 1;
solAssert( solAssert(
increment >= 0 && increment < _sourceUnitPaths.size(), increment >= 0 && increment < _sourceUnitPaths.size(),
"Solc custom mutator: Invalid increment" "Solc custom mutator: Invalid increment"
@ -104,7 +103,7 @@ void TestCaseGenerator::setup()
string TestCaseGenerator::visit() string TestCaseGenerator::visit()
{ {
ostringstream os; ostringstream os;
for (unsigned i = 0; i < PrngUtil{}.distributionOneToN(s_maxSourceUnits, rand); i++) for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxSourceUnits); i++)
{ {
string sourcePath = path(); string sourcePath = path();
os << "\n" os << "\n"
@ -140,7 +139,7 @@ string PragmaGenerator::visit()
)"; )";
// Choose equally at random from coder v1 and v2 // Choose equally at random from coder v1 and v2
string abiPragma = "pragma abicoder v" + string abiPragma = "pragma abicoder v" +
to_string(PrngUtil{}.distributionOneToN(2, rand)) + to_string(uRandDist->distributionOneToN(2)) +
";\n"; ";\n";
return preamble + abiPragma; return preamble + abiPragma;
} }
@ -157,7 +156,7 @@ string ImportGenerator::visit()
// there is one source unit present in test. // there is one source unit present in test.
if (state->size() == 1) if (state->size() == 1)
{ {
if (PrngUtil{}.probable(s_selfImportInvProb, rand)) if (uRandDist->probable(s_selfImportInvProb))
os << "import " os << "import "
<< "\"" << "\""
<< state->randomPath() << state->randomPath()
@ -186,9 +185,9 @@ shared_ptr<T> SolidityGenerator::generator()
SolidityGenerator::SolidityGenerator(unsigned _seed) SolidityGenerator::SolidityGenerator(unsigned _seed)
{ {
m_rand = make_shared<RandomEngine>(_seed);
m_generators = {}; m_generators = {};
m_state = make_shared<TestState>(m_rand); m_urd = make_shared<UniformRandomDistribution>(make_unique<RandomEngine>(_seed));
m_state = make_shared<TestState>(m_urd);
} }
template <size_t I> template <size_t I>

View File

@ -58,29 +58,34 @@ GENERATORLIST(VARIANTOFGENERATOR, COMMA(), )
using RandomEngine = std::mt19937_64; using RandomEngine = std::mt19937_64;
using Distribution = std::uniform_int_distribution<size_t>; using Distribution = std::uniform_int_distribution<size_t>;
struct GenerationProbability struct UniformRandomDistribution
{ {
explicit UniformRandomDistribution(std::unique_ptr<RandomEngine> _randomEngine):
randomEngine(std::move(_randomEngine))
{}
/// @returns an unsigned integer in the range [1, @param _n] chosen /// @returns an unsigned integer in the range [1, @param _n] chosen
/// uniformly at random. /// uniformly at random.
static size_t distributionOneToN(size_t _n, std::shared_ptr<RandomEngine> const& _rand) [[nodiscard]] size_t distributionOneToN(size_t _n) const
{ {
return Distribution(1, _n)(*_rand); return Distribution(1, _n)(*randomEngine);
} }
/// @returns true with a probability of 1/(@param _n), false otherwise. /// @returns true with a probability of 1/(@param _n), false otherwise.
/// @param _n must be non zero. /// @param _n must be non zero.
static bool probable(size_t _n, std::shared_ptr<RandomEngine> const& _rand) [[nodiscard]] bool probable(size_t _n) const
{ {
solAssert(_n > 0, ""); solAssert(_n > 0, "");
return distributionOneToN(_n, _rand) == 1; return distributionOneToN(_n) == 1;
} }
std::unique_ptr<RandomEngine> randomEngine;
}; };
struct TestState struct TestState
{ {
explicit TestState(std::shared_ptr<RandomEngine> _rand): explicit TestState(std::shared_ptr<UniformRandomDistribution> _urd):
sourceUnitPaths({}), sourceUnitPaths({}),
currentSourceUnitPath({}), currentSourceUnitPath({}),
rand(std::move(_rand)) uRandDist(std::move(_urd))
{} {}
/// Adds @param _path to @name sourceUnitPaths updates /// Adds @param _path to @name sourceUnitPaths updates
/// @name currentSourceUnitPath. /// @name currentSourceUnitPath.
@ -95,25 +100,25 @@ struct TestState
{ {
return sourceUnitPaths.empty(); return sourceUnitPaths.empty();
} }
/// Returns the number of items in @name sourceUnitPaths. /// @returns the number of items in @name sourceUnitPaths.
[[nodiscard]] size_t size() const [[nodiscard]] size_t size() const
{ {
return sourceUnitPaths.size(); return sourceUnitPaths.size();
} }
/// Prints test state to @param _os. /// Prints test state to @param _os.
void print(std::ostream& _os) const; void print(std::ostream& _os) const;
/// Returns a randomly chosen path from @param _sourceUnitPaths. /// @returns a randomly chosen path from @param _sourceUnitPaths.
[[nodiscard]] std::string randomPath(std::set<std::string> const& _sourceUnitPaths) const; [[nodiscard]] std::string randomPath(std::set<std::string> const& _sourceUnitPaths) const;
/// Returns a randomly chosen path from @name sourceUnitPaths. /// @returns a randomly chosen path from @name sourceUnitPaths.
[[nodiscard]] std::string randomPath() const; [[nodiscard]] std::string randomPath() const;
/// Returns a randomly chosen non current source unit path. /// @returns a randomly chosen non current source unit path.
[[nodiscard]] std::string randomNonCurrentPath() const; [[nodiscard]] std::string randomNonCurrentPath() const;
/// List of source paths in test input. /// List of source paths in test input.
std::set<std::string> sourceUnitPaths; std::set<std::string> sourceUnitPaths;
/// Source path being currently visited. /// Source path being currently visited.
std::string currentSourceUnitPath; std::string currentSourceUnitPath;
/// Random number generator. /// Uniform random distribution.
std::shared_ptr<RandomEngine> rand; std::shared_ptr<UniformRandomDistribution> uRandDist;
}; };
struct GeneratorBase struct GeneratorBase
@ -127,15 +132,15 @@ struct GeneratorBase
return std::get<std::shared_ptr<T>>(g); return std::get<std::shared_ptr<T>>(g);
solAssert(false, ""); solAssert(false, "");
} }
/// Returns test fragment created by this generator. /// @returns test fragment created by this generator.
std::string generate() std::string generate()
{ {
std::string generatedCode = visit(); std::string generatedCode = visit();
endVisit(); endVisit();
return generatedCode; return generatedCode;
} }
/// Virtual visitor that returns a string representing /// @returns a string representing the generation of
/// the generation of the Solidity grammar element. /// the Solidity grammar element.
virtual std::string visit() = 0; virtual std::string visit() = 0;
/// Method called after visiting this generator. Used /// Method called after visiting this generator. Used
/// for clearing state if necessary. /// for clearing state if necessary.
@ -162,12 +167,12 @@ struct GeneratorBase
} }
/// Shared pointer to the mutator instance /// Shared pointer to the mutator instance
std::shared_ptr<SolidityGenerator> mutator; std::shared_ptr<SolidityGenerator> mutator;
/// Random engine shared by Solidity mutators
std::shared_ptr<RandomEngine> rand;
/// Set of generators used by this generator. /// Set of generators used by this generator.
std::set<GeneratorPtr> generators; std::set<GeneratorPtr> generators;
/// Shared ptr to global test state. /// Shared ptr to global test state.
std::shared_ptr<TestState> state; std::shared_ptr<TestState> state;
/// Uniform random distribution
std::shared_ptr<UniformRandomDistribution> uRandDist;
}; };
class TestCaseGenerator: public GeneratorBase class TestCaseGenerator: public GeneratorBase
@ -184,7 +189,7 @@ public:
return "Test case generator"; return "Test case generator";
} }
private: private:
/// Returns a new source path name that is formed by concatenating /// @returns a new source path name that is formed by concatenating
/// a static prefix @name m_sourceUnitNamePrefix, a monotonically /// a static prefix @name m_sourceUnitNamePrefix, a monotonically
/// increasing counter starting from 0 and the postfix (extension) /// increasing counter starting from 0 and the postfix (extension)
/// ".sol". /// ".sol".
@ -252,18 +257,18 @@ class SolidityGenerator: public std::enable_shared_from_this<SolidityGenerator>
public: public:
explicit SolidityGenerator(unsigned _seed); explicit SolidityGenerator(unsigned _seed);
/// Returns the generator of type @param T. /// @returns the generator of type @param T.
template <typename T> template <typename T>
std::shared_ptr<T> generator(); std::shared_ptr<T> generator();
/// Returns a shared ptr to underlying random /// @returns a shared ptr to underlying random
/// number generator. /// number distribution.
std::shared_ptr<RandomEngine> randomEngine() std::shared_ptr<UniformRandomDistribution> uniformRandomDist()
{ {
return m_rand; return m_urd;
} }
/// Returns a pseudo randomly generated test case. /// @returns a pseudo randomly generated test case.
std::string generateTestProgram(); std::string generateTestProgram();
/// Returns shared ptr to global test state. /// @returns shared ptr to global test state.
std::shared_ptr<TestState> testState() std::shared_ptr<TestState> testState()
{ {
return m_state; return m_state;
@ -282,11 +287,11 @@ private:
{ {
m_generators.clear(); m_generators.clear();
} }
/// Random number generator
std::shared_ptr<RandomEngine> m_rand;
/// Sub generators /// Sub generators
std::set<GeneratorPtr> m_generators; std::set<GeneratorPtr> m_generators;
/// Shared global test state /// Shared global test state
std::shared_ptr<TestState> m_state; std::shared_ptr<TestState> m_state;
/// Uniform random distribution
std::shared_ptr<UniformRandomDistribution> m_urd;
}; };
} }