/*
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
/**
* Implements generators for synthesizing mostly syntactically valid
* Solidity test programs.
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
namespace solidity::test::fuzzer
{
/// Forward declarations
class SolidityGenerator;
/// Type declarations
#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
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;
struct GenerationProbability
{
enum class NumberLiteral
{
DECIMAL,
HEX
};
/// @returns an unsigned integer in the range [1, @param _n] chosen
/// uniformly at random.
static size_t distributionOneToN(size_t _n, std::shared_ptr _rand)
{
return Distribution(1, _n)(*_rand);
}
static bool chooseOneOfN(size_t _n, std::shared_ptr _rand)
{
return distributionOneToN(_n, _rand) == 1;
}
static std::string chooseOneOfNStrings(
std::vector const& _list,
std::shared_ptr _rand
)
{
return _list[GenerationProbability{}.distributionOneToN(_list.size(), _rand) - 1];
}
static std::string generateRandomAsciiString(size_t _length, std::shared_ptr _rand);
static std::string generateRandomHexString(size_t _length, std::shared_ptr _rand);
static std::pair generateRandomNumberLiteral(size_t _length, std::shared_ptr _rand);
};
struct AddDependenciesVisitor
{
template
void operator()(T const& _t)
{
_t->setup();
}
};
struct GeneratorVisitor
{
template
std::string operator()(T const& _t)
{
return _t->generate();
}
};
struct NameVisitor
{
template
std::string operator()(T const& _t)
{
return _t->name();
}
};
/// Forward declarations
#define SEMICOLON() ;
#define COMMA() ,
#define EMPTY()
#define FORWARDDECLAREGENERATORS(G) class G
GENERATORLIST(FORWARDDECLAREGENERATORS, SEMICOLON(), SEMICOLON())
#undef FORWARDDECLAREGENERATORS
class SolidityGenerator;
struct TestState;
/// Type declarations
using GeneratorPtr = std::variant<
#define VARIANTOFSHARED(G) std::shared_ptr
GENERATORLIST(VARIANTOFSHARED, COMMA(), EMPTY())
>;
#undef VARIANTOFSHARED
using Generator = std::variant<
#define VARIANTOFGENERATOR(G) G
GENERATORLIST(VARIANTOFGENERATOR, COMMA(), EMPTY())
>;
#undef VARIANTOFGENERATOR
#undef EMPTY
#undef COMMA
#undef SEMICOLON
struct GeneratorBase
{
GeneratorBase(std::shared_ptr _mutator);
template
std::shared_ptr generator()
{
for (auto& g: generators)
if (std::holds_alternative>(g))
return std::get>(g);
solAssert(false, "");
}
/// Returns test fragment created by this generator.
std::string generate()
{
std::string generatedCode = visit();
endVisit();
return generatedCode;
}
GeneratorPtr randomGenerator();
/// Virtual visitor that returns a string representing
/// the generation of the Solidity grammar element.
virtual std::string visit() = 0;
/// Method called after visiting this generator. Used
/// for clearing state if necessary.
virtual void endVisit() {}
/// Visitor that invokes child grammar elements of
/// this grammar element returning their string
/// representations.
std::string visitChildren();
/// Adds generators for child grammar elements of
/// this grammar element.
void addGenerators(std::set _generators)
{
generators += _generators;
}
/// Virtual method to obtain string name of generator.
virtual std::string name() = 0;
/// Virtual method to add generators that this grammar
/// element depends on. If not overridden, there are
/// no dependencies.
virtual void setup() {}
virtual ~GeneratorBase()
{
generators.clear();
}
/// Shared pointer to the mutator instance
std::shared_ptr mutator;
/// Random engine shared by Solidity mutators
std::shared_ptr rand;
/// Set of generators used by this generator.
std::set generators;
std::shared_ptr state;
};
//class FunctionTypeGenerator: public GeneratorBase
//{
//public:
// FunctionTypeGenerator(std::shared_ptr _mutator):
// GeneratorBase(std::move(_mutator))
// {}
// void setup() override;
// void reset() override {}
// std::string name() override
// {
// return "Function type generator";
// }
// std::string visit() override;
//private:
// static const std::vector s_visibility;
// std::string const m_functionTypeTemplate =
// std::string(R"(function () )") +
// R"( )" +
// R"( returns ())";
//};
//
//class UserDefinedTypeGenerator: public GeneratorBase
//{
//public:
// UserDefinedTypeGenerator(std::shared_ptr _mutator):
// GeneratorBase(std::move(_mutator))
// {}
// void setup() override;
// std::string visit() override;
// void reset() override {}
// std::string name() override
// {
// return "User defined type generator";
// }
//};
struct SolidityType
{
TYPE_ENUM_DECLS(Address, ADDRESS)
TYPE_ENUM_DECLS(Bool, BOOL)
TYPE_ENUM_DECLS(
Bytes,
BOOST_PP_REPEAT_FROM_TO(1, 34, BYTES_ENUM_ELEM, unused)
)
TYPE_ENUM_DECLS(
Integer,
BOOST_PP_REPEAT_FROM_TO(0, 32, INTEGER_ENUM_ELEM, INT),
BOOST_PP_REPEAT_FROM_TO(0, 32, INTEGER_ENUM_ELEM, UINT)
)
enum class TypeCategory: size_t
{
BOOL = 0,
ADDRESS,
INTEGER,
BYTES,
TYPEMAX
};
using Type = std::variant;
struct TypeStringVisitor
{
template
std::string operator()(T const& _type)
{
return toString(_type);
}
};
struct TypeIndexVisitor
{
template
size_t operator()(T const& _type)
{
return static_cast(_type);
}
};
SolidityType(TypeCategory _type, std::shared_ptr _rand):
typeCategory(_type),
randomEngine(std::move(_rand))
{
switch (typeCategory)
{
case TypeCategory::ADDRESS:
type = indexedAddressType(0);
break;
case TypeCategory::BOOL:
type = indexedBoolType(0);
break;
case TypeCategory::BYTES:
type = indexedBytesType(GenerationProbability{}.distributionOneToN(size_t(Bytes::BYTES) + 1, randomEngine) - 1);
break;
case TypeCategory::INTEGER:
type = indexedIntegerType(GenerationProbability{}.distributionOneToN(size_t(Integer::UINT256) + 1, randomEngine) - 1);
break;
case TypeCategory::TYPEMAX:
solAssert(false, "");
}
}
void setValueType()
{
switch (typeCategory)
{
case TypeCategory::ADDRESS:
type = indexedAddressType(0);
break;
case TypeCategory::BOOL:
type = indexedBoolType(0);
break;
case TypeCategory::BYTES:
type = indexedBytesType(GenerationProbability{}.distributionOneToN(size_t(Bytes::BYTES), randomEngine) - 1);
break;
case TypeCategory::INTEGER:
type = indexedIntegerType(GenerationProbability{}.distributionOneToN(size_t(Integer::UINT256) + 1, randomEngine) - 1);
break;
case TypeCategory::TYPEMAX:
solAssert(false, "");
}
}
void setNonValueType()
{
type = indexedBytesType(size_t(Bytes::BYTES));
}
virtual ~SolidityType() = default;
TypeCategory typeCategory;
std::pair type;
std::shared_ptr randomEngine;
};
class ExpressionGenerator: public GeneratorBase
{
public:
enum Type
{
INDEXACCESS = 0ul,
INDEXRANGEACCESS,
MEMBERACCESS,
FUNCTIONCALLOPTIONS,
FUNCTIONCALL,
PAYABLECONVERSION,
METATYPE,
UNARYPREFIXOP,
UNARYSUFFIXOP,
EXPOP,
MULDIVMODOP,
ADDSUBOP,
SHIFTOP,
BITANDOP,
BITXOROP,
BITOROP,
ORDERCOMPARISON,
EQUALITYCOMPARISON,
ANDOP,
OROP,
CONDITIONAL,
ASSIGNMENT,
NEWEXPRESSION,
TUPLE,
INLINEARRAY,
IDENTIFIER,
LITERAL,
ELEMENTARYTYPENAME,
USERDEFINEDTYPENAME,
TYPEMAX
};
ExpressionGenerator(
std::shared_ptr _mutator,
bool _compileTimeConstantExpressionsOnly = false
):
GeneratorBase(std::move(_mutator)),
m_expressionNestingDepth(0),
m_compileTimeConstantExpressionsOnly(_compileTimeConstantExpressionsOnly),
m_type(randomTypeCategory((*rand)()), rand)
{}
void setup() override {}
std::string visit() override;
void endVisit() override
{
m_expressionNestingDepth = 0;
}
std::string name() override
{
return "Expression Generator";
}
std::string typeString()
{
return m_type.type.second;
}
std::string randomTypeString()
{
return SolidityType(randomTypeCategory((*rand)()), rand).type.second;
}
std::shared_ptr randomType()
{
return std::make_shared(
SolidityType(randomTypeCategory((*rand)()), rand)
);
}
void setType(std::shared_ptr _type)
{
m_type = *_type;
}
void setType(SolidityType _type)
{
m_type = _type;
}
void setValueType()
{
m_type.setValueType();
}
void setNonValueType()
{
m_type.setNonValueType();
}
private:
static SolidityType::TypeCategory randomTypeCategory(size_t _pseudoRandomNumber)
{
return static_cast(
_pseudoRandomNumber % static_cast(SolidityType::TypeCategory::TYPEMAX)
);
}
std::string identifier();
std::string boolLiteral()
{
return GenerationProbability{}.chooseOneOfN(2, rand) ? "true" : "false";
}
std::string doubleQuotedStringLiteral();
std::string hexLiteral();
std::string numberLiteral();
std::string addressLiteral();
std::string literal();
std::string expression();
void incrementNestingDepth()
{
m_expressionNestingDepth++;
}
bool nestingDepthTooHigh()
{
return m_expressionNestingDepth > s_maxNumNestedExpressions;
}
size_t m_expressionNestingDepth;
bool m_compileTimeConstantExpressionsOnly;
SolidityType m_type;
static constexpr size_t s_maxNumNestedExpressions = 5;
static constexpr size_t s_maxStringLength = 10;
static constexpr size_t s_maxHexLiteralLength = 64;
static constexpr size_t s_maxElementsInTuple = 4;
static constexpr size_t s_maxElementsInlineArray = 4;
};
class StateVariableDeclarationGenerator: public GeneratorBase
{
public:
enum Visibility
{
PUBLIC = 0,
PRIVATE,
INTERNAL,
VISIBILITYMAX
};
StateVariableDeclarationGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "StateVariableDeclarationGenerator";
}
private:
std::string identifier()
{
return "sv" + std::to_string(GenerationProbability{}.distributionOneToN(s_maxStateVariables, rand));
}
std::string visibility();
static constexpr size_t s_maxStateVariables = 3;
std::string const m_declarationTemplate =
std::string(R"()") +
R"( constant immutable = ;)";
};
struct Exports
{
Exports(std::string& _path): sourceUnitPath(_path), symbols({}), types({})
{}
/// Source unit path
std::string sourceUnitPath;
/// Exported symbols
std::set symbols;
/// Exported user defined types
std::set types;
};
struct ExportedSymbols
{
ExportedSymbols(): symbols({}), types({})
{}
ExportedSymbols& operator+=(ExportedSymbols& _right)
{
for (auto item: _right.symbols)
if (!symbols.count(item))
symbols.emplace(item);
for (auto item: _right.types)
if (!types.count(item))
types.emplace(item);
return *this;
}
ExportedSymbols& operator+=(std::string& _right)
{
if (!symbols.count(_right))
symbols.emplace(_right);
if (!types.count(_right))
types.emplace(_right);
return *this;
}
void removeSymbolsAndTypes()
{
symbols.clear();
types.clear();
}
std::string randomSymbol(std::shared_ptr _rand);
std::string randomUserDefinedType(std::shared_ptr _rand);
std::set symbols;
std::set types;
};
struct State
{
virtual ~State() {}
};
struct FunctionState: State
{
~FunctionState()
{
std::cout << "Destroying function state" << std::endl;
inputParameters.clear();
returnParameters.clear();
locals.clear();
}
/// Parameter type, name pair
using ParamType = std::pair, std::string>;
enum class Mutability
{
PURE,
VIEW,
PAYABLE,
NONPAYABLE,
};
enum class Visibility
{
EXTERNAL,
INTERNAL,
PUBLIC,
PRIVATE
};
enum class Inheritance
{
VIRTUAL,
OVERRIDE,
VIRTUALOVERRIDE,
NONE
};
Mutability randomMutability(std::shared_ptr _rand)
{
switch (GenerationProbability{}.distributionOneToN(4, _rand))
{
case 1:
return Mutability::PURE;
case 2:
return Mutability::VIEW;
case 3:
return Mutability::PAYABLE;
case 4:
return Mutability::NONPAYABLE;
}
solAssert(false, "");
}
Mutability randomFreeFunctionMutability(std::shared_ptr _rand)
{
switch (GenerationProbability{}.distributionOneToN(3, _rand))
{
case 1:
return Mutability::PURE;
case 2:
return Mutability::VIEW;
case 3:
return Mutability::NONPAYABLE;
}
solAssert(false, "");
}
void setName(std::string _name)
{
name = _name;
}
void setMutability(Mutability _mut)
{
mutability = _mut;
}
void setVisibility(Visibility _vis)
{
visibility = _vis;
}
void setParameterTypes(std::vector _paramTypes)
{
inputParameters = std::move(_paramTypes);
}
void setReturnTypes(std::vector _returnTypes)
{
returnParameters = std::move(_returnTypes);
}
void addReturn(std::shared_ptr& _returnType)
{
returnParameters.push_back({_returnType, "r" + std::to_string(numReturns++)});
}
void addInput(std::shared_ptr& _inputType)
{
inputParameters.push_back({_inputType, "i" + std::to_string(numInputs++)});
}
void addVariable(std::shared_ptr& _variableType)
{
locals.push_back({_variableType, "v" + std::to_string(numLocals++)});
}
void setInheritance(Inheritance _inh)
{
inheritance = _inh;
}
bool identifiers()
{
return numReturns > 0 || numInputs > 0 || numLocals > 0;
}
bool operator==(FunctionState const& _other);
std::string name;
Mutability mutability;
Visibility visibility;
size_t numInputs;
size_t numReturns;
size_t numLocals;
std::vector inputParameters;
std::vector returnParameters;
std::vector locals;
Inheritance inheritance;
};
struct SourceUnitState: State
{
SourceUnitState(): exportedSymbols({})
{}
~SourceUnitState()
{
std::cout << "Destroying source unit state" << std::endl;
functions.clear();
exportedSymbols.removeSymbolsAndTypes();
}
void exportSymbol(std::string& _symbol)
{
exportedSymbols += _symbol;
}
void exportSymbols(ExportedSymbols& _symbols)
{
exportedSymbols += _symbols;
}
void enterFunction(std::shared_ptr _function)
{
exportedSymbols += _function->name;
functions.emplace_back(_function);
}
void leaveFunction()
{
functions.pop_back();
}
std::shared_ptr currentFunction()
{
if (functions.size() > 0)
return functions[functions.size() - 1];
else
return nullptr;
}
bool functionExists(std::shared_ptr _function)
{
for (auto const& f: functions)
if (f == _function)
return true;
return false;
}
bool symbols()
{
return exportedSymbols.symbols.size() > 0;
}
bool userDefinedTypes()
{
return exportedSymbols.types.size() > 0;
}
ExportedSymbols exportedSymbols;
std::vector> functions;
};
struct ImportState
{
/// Maps a symbol to its alias identifier
using SymbolAliases = std::map;
/// A single alias identifier for all symbols
using UnitAlias = std::string;
/// An alias is optional, when present it is either
/// a single identifier or a mapping of symbols to
/// their respective alias identifiers.
using Alias = std::optional>;
ImportState(std::string&& _path, std::set&& _symbols, Alias _alias):
path(_path),
symbols(std::move(_symbols)),
aliases(std::move(_alias))
{}
/// Import path
std::string path;
/// Imported symbols
std::set symbols;
/// Alias representation
Alias aliases;
};
struct TestState: State
{
TestState(std::shared_ptr _rand):
sourceUnitStates({}),
currentSourceName({}),
rand(std::move(_rand))
{}
~TestState()
{
std::cout << "Destroying test state" << std::endl;
}
void addSourceUnit(std::string& _path)
{
sourceUnitStates.emplace(_path, SourceUnitState{});
currentSourceName = _path;
}
bool empty()
{
return sourceUnitStates.empty();
}
size_t size()
{
return sourceUnitStates.size();
}
void print();
std::string randomPath();
std::string randomNonCurrentPath();
std::string currentSourceUnit()
{
return currentSourceName;
}
SourceUnitState& currentSourceState()
{
return sourceUnitStates[currentSourceUnit()];
}
void removeSourceStates()
{
sourceUnitStates.clear();
}
std::map sourceUnitStates;
std::string currentSourceName;
std::shared_ptr rand;
};
class TestCaseGenerator: public GeneratorBase
{
public:
TestCaseGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator)),
m_numSourceUnits(0)
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "Test case generator";
}
private:
bool empty()
{
return m_numSourceUnits == 0;
}
std::string randomPath();
std::shared_ptr testState()
{
return state;
}
std::string path(size_t _number) const
{
return m_sourceUnitNamePrefix + std::to_string(_number) + ".sol";
}
std::string path() const
{
return m_sourceUnitNamePrefix + std::to_string(m_numSourceUnits) + ".sol";
}
void addSourceUnit(std::string& _path)
{
state->addSourceUnit(_path);
}
/// Number of source units in test input
size_t m_numSourceUnits;
/// String prefix of source unit names
std::string const m_sourceUnitNamePrefix = "su";
/// Maximum number of source units per test input
static constexpr unsigned s_maxSourceUnits = 3;
};
class PragmaGenerator: public GeneratorBase
{
public:
PragmaGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
std::string visit() override;
std::string name() override { return "Pragma generator"; }
};
class ImportGenerator: public GeneratorBase
{
public:
ImportGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
std::string visit() override;
std::string name() override { return "Import generator"; }
private:
std::vector m_globalExports;
std::string const m_importPathAs = R"(import "" as ;)";
std::string const m_importStar = R"(import * as from "";)";
std::string const m_alias = R"( as )";
std::string const m_importSymAliases = R"(import {} from "";)";
static constexpr size_t s_selfImportInvProb = 101;
};
struct InterfaceFunction
{
// FunctionMutability mutability;
};
struct InterfaceState
{
};
struct ContractState
{
ContractState():
baseContractStates({}),
functionStates({})
{}
void addBaseContract();
void addFunction();
std::vector> baseContractStates;
std::vector> functionStates;
};
struct Expression
{
std::string visit()
{
return expressionTemplate;
}
std::string const expressionTemplate = R"(1)";
};
struct NamedArgument
{
std::string visit()
{
return identifier + ": " + expression.visit();
}
std::string identifier;
Expression expression;
};
struct NamedArgumentList
{
std::set namedArguments;
std::string const namedTemplate = R"({})";
};
struct CallArgument
{
using Argument = std::variant;
Argument argument;
};
struct CallArgumentList
{
std::vector callArguments;
};
struct InheritanceSpecifier
{
std::string name;
std::optional callArguments;
};
struct InheritanceSpecifierList
{
std::set inheritanceSpecifier;
};
struct Location
{
enum class Loc
{
MEMORY,
STORAGE,
CALLDATA,
STACK
};
Location(Loc _l): loc(_l) {}
Loc loc;
std::string visit();
};
class LocationGenerator: public GeneratorBase
{
public:
LocationGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
std::string visit() override;
std::string name() override
{
return "LocationGenerator";
}
};
struct IntegerWidth
{
IntegerWidth(unsigned _w)
{
width = (8 * _w) % 256;
}
std::string visit()
{
return width > 0 ? std::to_string(width) : std::string("256");
}
unsigned width;
};
struct Statement
{
virtual ~Statement() {}
virtual std::string visit() = 0;
};
struct VariableDeclaration
{
VariableDeclaration(std::shared_ptr _type, Location _loc, std::string&& _id):
type(std::move(_type)),
location(_loc),
identifier(std::move(_id))
{}
std::shared_ptr type;
Location location;
std::string identifier;
std::string visit();
std::string const varDeclTemplate = R"( ;)";
};
class VariableDeclarationGenerator: public GeneratorBase
{
public:
VariableDeclarationGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string name() override
{
return "VariableDeclarationGenerator";
}
std::string visit() override;
private:
std::string identifier();
};
struct ParameterListState
{
};
class ParameterListGenerator: public GeneratorBase
{
public:
ParameterListGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "ParameterListGenerator";
}
};
struct ParameterList
{
std::vector params;
std::string const parameterListTemplate = R"()";
};
struct VariableDeclarationTuple
{
std::vector varDecls;
std::string const varDeclTupleTemplate =
R"()";
};
struct ExpressionStatement: Statement
{
ExpressionStatement()
{
}
std::string visit() override;
Expression expression;
std::string const exprStmtTemplate = R"(;)";
};
struct VariableDeclarationTupleAssignment: Statement
{
VariableDeclarationTuple tuple;
Expression expression;
std::string visit() override;
std::string const varDeclTupleAssignTemplate =
R"( = ;)";
};
class SimpleVarDeclGenerator: public GeneratorBase
{
public:
SimpleVarDeclGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "Simple var decl statement generator";
}
std::string const simpleVarDeclTemplate =
R"( = )";
};
struct GeneratorTypeVisitor
{
template
std::string operator()(T& _value)
{
return _value.visit();
}
};
//struct VariableDeclarationStatement: Statement
//{
// using VarDeclStmt = std::variant;
// VariableDeclarationStatement(VarDeclStmt _stmt): stmt(std::move(_stmt))
// {}
// VarDeclStmt stmt;
// std::string visit() override
// {
// return std::visit(GeneratorTypeVisitor{}, stmt);
// }
//};
//struct SimpleStatement: Statement
//{
// using Stmt = std::variant;
// SimpleStatement(Stmt _stmt): statement(std::move(_stmt))
// {}
// Stmt statement;
// std::string visit() override
// {
// return std::visit(GeneratorTypeVisitor{}, statement);
// }
//};
/// Forward declaration
//using StatementTy = std::variant;
class StatementGenerator: public GeneratorBase
{
public:
enum class Type: size_t
{
BLOCK = 0,
SIMPLE,
IF,
FOR,
WHILE,
DOWHILE,
CONTINUE,
BREAK,
TRY,
RETURN,
EMIT,
ASSEMBLY,
TYPEMAX
};
enum class YulStmtType: size_t
{
BLOCK = 0,
VARDECL,
ASSIGN,
FUNCTIONCALL,
IF,
FOR,
SWITCH,
LEAVE,
BREAK,
CONTINUE,
FUNCTIONDEF
};
StatementGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator)),
m_type(randomType((*rand)())),
m_statementNestingDepth(0),
m_numVariables(0),
m_loop(false)
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "Block statement generator";
}
private:
std::string variableName()
{
return "v" + std::to_string(m_numVariables++);
}
std::string statement();
static Type randomType(size_t _randomNumber)
{
return static_cast(
_randomNumber % static_cast(Type::TYPEMAX)
);
}
void incrementNestingDepth()
{
m_statementNestingDepth++;
}
bool nestingDepthTooHigh()
{
return m_statementNestingDepth > s_maxNumNestedStatements;
}
std::string simpleStatement();
std::string blockStatement();
Type m_type;
size_t m_statementNestingDepth;
size_t m_numVariables;
bool m_loop;
static size_t constexpr s_maxNumNestedStatements = 5;
};
class EnumDeclaration: public GeneratorBase
{
public:
EnumDeclaration(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override {}
std::string visit() override;
std::string name() override
{
return "Enum generator";
}
private:
std::string enumName()
{
return "E" + std::to_string((*rand)() % s_maxIdentifiers);
}
std::string const enumTemplate = R"(enum { })";
static constexpr size_t s_maxMembers = 5;
static constexpr size_t s_maxIdentifiers = 4;
};
class ConstantVariableDeclaration: public GeneratorBase
{
public:
ConstantVariableDeclaration(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override { return "Constant variable generator"; }
private:
std::string const constantVarDeclTemplate =
R"( constant = ;)";
};
class FallbackDefinitionGenerator: public GeneratorBase
{
public:
FallbackDefinitionGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override
{
return "FallbackDefinitionGenerator";
}
};
class FunctionDefinitionGenerator: public GeneratorBase
{
public:
FunctionDefinitionGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{}
void setup() override;
std::string visit() override;
std::string name() override { return "Function generator"; }
void freeFunctionMode()
{
m_freeFunction = true;
}
void contractFunctionMode()
{
m_freeFunction = false;
}
static const std::vector s_mutability;
private:
std::string functionIdentifier();
std::shared_ptr m_state;
std::shared_ptr m_functionState;
bool m_freeFunction;
static const std::vector s_visibility;
static const std::vector s_freeFunctionMutability;
std::string const m_functionTemplate =
R"()" +
std::string(R"(function () )") +
R"( )" +
R"( returns ())" +
R"(;)";
};
class ContractDefinitionGenerator: public GeneratorBase
{
public:
ContractDefinitionGenerator(std::shared_ptr _generator):
GeneratorBase(std::move(_generator))
{}
void setup() override;
std::string visit() override;
std::string name() override { return "Contract generator"; }
private:
std::optional m_inheritanceList;
const std::string m_contractTemplate =
R"()" +
std::string(R"(abstract contract )") +
R"( is { })";
/// List of inverse probabilities of sub-components
static constexpr size_t s_abstractInvProb = 10;
static constexpr size_t s_inheritanceInvProb = 10;
};
class SourceUnitGenerator: public GeneratorBase
{
public:
SourceUnitGenerator(std::shared_ptr _mutator):
GeneratorBase(std::move(_mutator))
{
m_sourceState = std::make_shared();
}
void setup() override;
std::string visit() override;
std::string name() override { return "Source unit generator"; }
private:
void saveState();
std::shared_ptr m_testState;
std::shared_ptr m_sourceState;
static constexpr unsigned s_maxElements = 10;
};
class NatSpecGenerator: public GeneratorBase
{
public:
enum class TagCategory
{
CONTRACT,
FUNCTION,
PUBLICSTATEVAR,
EVENT
};
enum class Tag
{
TITLE,
AUTHOR,
NOTICE,
DEV,
PARAM,
RETURN,
INHERITDOC
};
NatSpecGenerator(std::shared_ptr _generator): GeneratorBase(std::move(_generator))
{
m_nestingDepth = 0;
}
void setup() override {}
std::string visit() override;
void endVisit() override
{
m_nestingDepth = 0;
}
std::string name() override { return "NatSpec generator"; }
void tagCategory(TagCategory _tag)
{
m_tag = _tag;
}
private:
std::string randomNatSpecString(TagCategory _category);
Tag randomTag(TagCategory _category);
TagCategory m_tag;
size_t m_nestingDepth;
static std::map> s_tagLookup;
static constexpr size_t s_maxTextLength = 8;
static constexpr size_t s_maxNestedTags = 3;
std::string const m_tagTemplate = R"( )";
};
struct InterfaceSpecifiers
{
std::set typeNames;
};
struct SourceState {
unsigned numPragmas;
unsigned numImports;
unsigned numContracts;
unsigned numAbstractContracts;
unsigned numInterfaces;
unsigned numLibraries;
unsigned numGlobalStructs;
unsigned numGlobalFuncs;
unsigned numGlobalEnums;
};
struct ProgramState
{
enum class ContractType
{
CONTRACT,
ABSTRACTCONTRACT,
INTERFACE,
LIBRARY
};
unsigned numFunctions;
unsigned numModifiers;
unsigned numContracts;
unsigned numLibraries;
unsigned numInterfaces;
unsigned numStructs;
unsigned numEvents;
bool constructorDefined;
ContractType contractType;
};
class SolidityGenerator: public std::enable_shared_from_this
{
public:
explicit SolidityGenerator(unsigned _seed);
/// Returns a multi-source test case.
std::string visit();
/// Returns the generator of type @param T.
template
std::shared_ptr generator();
/// Returns a shared ptr to underlying random
/// number generator.
std::shared_ptr randomEngine()
{
return m_rand;
}
std::shared_ptr testState()
{
return m_state;
}
/// Returns a pseudo randomly generated test case.
std::string generateTestProgram();
private:
template
void createGenerator()
{
m_generators.insert(
std::make_shared(shared_from_this())
);
}
template
void createGenerators();
void destroyGenerators()
{
m_generators.clear();
}
void destroyState()
{
m_state->removeSourceStates();
}
void initialize();
/// Random number generator
std::shared_ptr m_rand;
/// Sub generators
std::set m_generators;
/// Test state
std::shared_ptr m_state;
};
}