diff --git a/test/tools/ossfuzz/Generators.h b/test/tools/ossfuzz/Generators.h index 095195f3c..9074c895a 100644 --- a/test/tools/ossfuzz/Generators.h +++ b/test/tools/ossfuzz/Generators.h @@ -42,6 +42,7 @@ */ #define GENERATORLIST(MACRO, SEP, ENDSEP) \ MACRO(ContractGenerator) SEP \ + MACRO(FunctionGenerator) SEP \ MACRO(ImportGenerator) SEP \ MACRO(PragmaGenerator) SEP \ MACRO(SourceUnitGenerator) SEP \ diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp index cb89ed756..5b50cb82a 100644 --- a/test/tools/ossfuzz/SolidityGenerator.cpp +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -135,7 +135,8 @@ void SourceUnitGenerator::setup() addGenerators({ {mutator->generator(), s_maxImports}, {mutator->generator(), 1}, - {mutator->generator(), 1} + {mutator->generator(), 1}, + {mutator->generator(), s_maxFreeFunctions} }); } @@ -185,11 +186,43 @@ string ImportGenerator::visit() return os.str(); } +void ContractGenerator::setup() +{ + addGenerators({ + {mutator->generator(), s_maxFunctions} + }); +} + string ContractGenerator::visit() { + ScopeGuard reset([&]() { + mutator->generator()->scope(true); + state->unindent(); + }); + auto set = [&]() { + state->indent(); + mutator->generator()->scope(false); + }; + ostringstream os; string name = state->newContract(); state->updateContract(name); - return "contract " + name + " {}\n"; + os << "contract " << name << " {" << endl; + set(); + os << visitChildren(); + os << "}" << endl; + return os.str(); +} + +string FunctionGenerator::visit() +{ + string visibility; + string name = state->newFunction(); + state->updateFunction(name); + if (!m_freeFunction) + visibility = "public"; + + return indentation(state->indentationLevel) + + "function " + name + "() " + visibility + " pure {}\n"; } template diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h index f51f85ce7..1ee9f04d8 100644 --- a/test/tools/ossfuzz/SolidityGenerator.h +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -116,8 +116,17 @@ struct SourceState { explicit SourceState(std::shared_ptr _urd): uRandDist(std::move(_urd)), - importedSources({}) + importedSources({}), + freeFunctions({}) {} + void addFreeFunction(std::string& _functionName) + { + freeFunctions.emplace(_functionName); + } + bool freeFunction(std::string const& _functionName) + { + return freeFunctions.count(_functionName); + } void addImportedSourcePath(std::string& _sourcePath) { importedSources.emplace(_sourcePath); @@ -134,17 +143,24 @@ struct SourceState void print(std::ostream& _os) const; std::shared_ptr uRandDist; std::set importedSources; + std::set freeFunctions; }; +struct FunctionState {}; + struct TestState { explicit TestState(std::shared_ptr _urd): sourceUnitState({}), contractState({}), currentSourceUnitPath({}), + currentContract({}), + currentFunction({}), uRandDist(std::move(_urd)), numSourceUnits(0), - numContracts(0) + numContracts(0), + numFunctions(0), + indentationLevel(0) {} /// Adds @param _path to @name sourceUnitPaths updates /// @name currentSourceUnitPath. @@ -160,6 +176,11 @@ struct TestState contractState.emplace(_name, std::make_shared(uRandDist)); currentContract = _name; } + void addFunction(std::string const& _name) + { + functionState.emplace(_name, std::make_shared()); + currentFunction = _name; + } /// Returns true if @name sourceUnitPaths is empty, /// false otherwise. [[nodiscard]] bool empty() const @@ -183,6 +204,10 @@ struct TestState { return contractPrefix + std::to_string(numContracts); } + [[nodiscard]] std::string newFunction() const + { + return functionPrefix + std::to_string(numFunctions); + } [[nodiscard]] std::string currentPath() const { solAssert(numSourceUnits > 0, ""); @@ -202,10 +227,25 @@ struct TestState addContract(_name); numContracts++; } + void updateFunction(std::string const& _name) + { + addFunction(_name); + numFunctions++; + } void addSource() { updateSourcePath(newPath()); } + /// Increments indentation level globally. + void indent() + { + ++indentationLevel; + } + /// Decrements indentation level globally. + void unindent() + { + --indentationLevel; + } ~TestState() { sourceUnitState.clear(); @@ -224,20 +264,30 @@ struct TestState std::map> sourceUnitState; /// Map of contract name -> state std::map> contractState; + /// Map of function name -> state + std::map> functionState; /// Source path being currently visited. std::string currentSourceUnitPath; /// Current contract std::string currentContract; + /// Current function + std::string currentFunction; /// Uniform random distribution. std::shared_ptr uRandDist; /// Number of source units in test input size_t numSourceUnits; /// Number of contracts in test input size_t numContracts; + /// Number of functions in test input + size_t numFunctions; + /// Indentation level + unsigned indentationLevel; /// Source name prefix std::string const sourceUnitNamePrefix = "su"; /// Contract name prefix std::string const contractPrefix = "C"; + /// Function name prefix + std::string const functionPrefix = "f"; }; struct GeneratorBase @@ -258,6 +308,12 @@ struct GeneratorBase endVisit(); return generatedCode; } + /// @returns indentation as string. Each indentation level comprises two + /// whitespace characters. + std::string indentation(unsigned _indentationLevel) + { + return std::string(_indentationLevel * 2, ' '); + } /// @returns a string representing the generation of /// the Solidity grammar element. virtual std::string visit() = 0; @@ -341,6 +397,7 @@ public: std::string name() override { return "Source unit generator"; } private: static unsigned constexpr s_maxImports = 2; + static unsigned constexpr s_maxFreeFunctions = 2; }; class PragmaGenerator: public GeneratorBase @@ -378,8 +435,30 @@ public: explicit ContractGenerator(std::shared_ptr _mutator): GeneratorBase(std::move(_mutator)) {} + void setup() override; std::string visit() override; std::string name() override { return "Contract generator"; } +private: + static unsigned constexpr s_maxFunctions = 4; +}; + +class FunctionGenerator: public GeneratorBase +{ +public: + explicit FunctionGenerator(std::shared_ptr _mutator): + GeneratorBase(std::move(_mutator)), + m_freeFunction(true) + {} + std::string visit() override; + std::string name() override { return "Function generator"; } + /// Sets @name m_freeFunction to @param _freeFunction. + void scope(bool _freeFunction) + { + m_freeFunction = _freeFunction; + } +private: + bool m_freeFunction; + }; class SolidityGenerator: public std::enable_shared_from_this