#pragma once
#include <test/tools/ossfuzz/Generators.h>
#include <liblangutil/Exceptions.h>
#include <memory>
#include <random>
#include <set>
#include <variant>
namespace solidity::test::fuzzer::mutator
{
class SolidityGenerator;
#define SEMICOLON() ;
#define FORWARDDECLAREGENERATORS(G) class G
GENERATORLIST(FORWARDDECLAREGENERATORS, SEMICOLON(), SEMICOLON())
#undef FORWARDDECLAREGENERATORS
#undef SEMICOLON
#define COMMA() ,
using GeneratorPtr = std::variant<
#define VARIANTOFSHARED(G) std::shared_ptr<G>
GENERATORLIST(VARIANTOFSHARED, COMMA(), )
>;
#undef VARIANTOFSHARED
using Generator = std::variant<
#define VARIANTOFGENERATOR(G) G
GENERATORLIST(VARIANTOFGENERATOR, COMMA(), )
>;
#undef VARIANTOFGENERATOR
#undef COMMA
using RandomEngine = std::mt19937_64;
using Distribution = std::uniform_int_distribution<size_t>;
struct UniformRandomDistribution
{
explicit UniformRandomDistribution(std::unique_ptr<RandomEngine> _randomEngine):
randomEngine(std::move(_randomEngine))
{}
[[nodiscard]] size_t distributionOneToN(size_t _n) const
{
return Distribution(1, _n)(*randomEngine);
}
[[nodiscard]] bool probable(size_t _n) const
{
solAssert(_n > 0, "");
return distributionOneToN(_n) == 1;
}
std::unique_ptr<RandomEngine> randomEngine;
};
struct TestState
{
explicit TestState(std::shared_ptr<UniformRandomDistribution> _urd):
sourceUnitPaths({}),
currentSourceUnitPath({}),
uRandDist(std::move(_urd))
{}
void addSourceUnit(std::string const& _path)
{
sourceUnitPaths.insert(_path);
currentSourceUnitPath = _path;
}
[[nodiscard]] bool empty() const
{
return sourceUnitPaths.empty();
}
[[nodiscard]] size_t size() const
{
return sourceUnitPaths.size();
}
void print(std::ostream& _os) const;
[[nodiscard]] std::string randomPath(std::set<std::string> const& _sourceUnitPaths) const;
[[nodiscard]] std::string randomPath() const;
[[nodiscard]] std::string randomNonCurrentPath() const;
std::set<std::string> sourceUnitPaths;
std::string currentSourceUnitPath;
std::shared_ptr<UniformRandomDistribution> uRandDist;
};
struct GeneratorBase
{
explicit GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator);
template <typename T>
std::shared_ptr<T> generator()
{
for (auto& g: generators)
if (std::holds_alternative<std::shared_ptr<T>>(g))
return std::get<std::shared_ptr<T>>(g);
solAssert(false, "");
}
std::string generate()
{
std::string generatedCode = visit();
endVisit();
return generatedCode;
}
virtual std::string visit() = 0;
virtual void endVisit() {}
std::string visitChildren();
void addGenerators(std::set<GeneratorPtr> _generators)
{
generators += _generators;
}
virtual std::string name() = 0;
virtual void setup() {}
virtual ~GeneratorBase()
{
generators.clear();
}
std::shared_ptr<SolidityGenerator> mutator;
std::set<GeneratorPtr> generators;
std::shared_ptr<TestState> state;
std::shared_ptr<UniformRandomDistribution> uRandDist;
};
class TestCaseGenerator: public GeneratorBase
{
public:
explicit TestCaseGenerator(std::shared_ptr<SolidityGenerator> _mutator):
GeneratorBase(std::move(_mutator)),
m_numSourceUnits(0)
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "Test case generator";
}
private:
[[nodiscard]] std::string path() const
{
return m_sourceUnitNamePrefix + std::to_string(m_numSourceUnits) + ".sol";
}
void updateSourcePath(std::string const& _path)
{
state->addSourceUnit(_path);
m_numSourceUnits++;
}
size_t m_numSourceUnits;
std::string const m_sourceUnitNamePrefix = "su";
static constexpr unsigned s_maxSourceUnits = 3;
};
class SourceUnitGenerator: public GeneratorBase
{
public:
explicit SourceUnitGenerator(std::shared_ptr<SolidityGenerator> _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override { return "Source unit generator"; }
};
class PragmaGenerator: public GeneratorBase
{
public:
explicit PragmaGenerator(std::shared_ptr<SolidityGenerator> _mutator):
GeneratorBase(std::move(_mutator))
{}
std::string visit() override;
std::string name() override { return "Pragma generator"; }
};
class ImportGenerator: public GeneratorBase
{
public:
explicit ImportGenerator(std::shared_ptr<SolidityGenerator> _mutator):
GeneratorBase(std::move(_mutator))
{}
std::string visit() override;
std::string name() override { return "Import generator"; }
private:
static constexpr size_t s_selfImportInvProb = 17;
};
class SolidityGenerator: public std::enable_shared_from_this<SolidityGenerator>
{
public:
explicit SolidityGenerator(unsigned _seed);
template <typename T>
std::shared_ptr<T> generator();
std::shared_ptr<UniformRandomDistribution> uniformRandomDist()
{
return m_urd;
}
std::string generateTestProgram();
std::shared_ptr<TestState> testState()
{
return m_state;
}
private:
template <typename T>
void createGenerator()
{
m_generators.insert(
std::make_shared<T>(shared_from_this())
);
}
template <std::size_t I = 0>
void createGenerators();
void destroyGenerators()
{
m_generators.clear();
}
std::set<GeneratorPtr> m_generators;
std::shared_ptr<TestState> m_state;
std::shared_ptr<UniformRandomDistribution> m_urd;
};
}