diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp index e3d222456..0c5a55322 100644 --- a/test/tools/ossfuzz/SolidityGenerator.cpp +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -230,8 +230,52 @@ string FunctionGenerator::visit() if (!m_freeFunction) visibility = "public"; + auto inputType = TypeGenerator{state}.type(); + state->currentFunctionState()->addInput(pair>("i1", inputType)); + string inputParams = inputType->toString() + " i1"; return indentation(state->indentationLevel) + - "function " + name + "() " + visibility + " pure {}\n"; + "function " + name + "(" + inputParams + ") " + visibility + " pure {}\n"; +} + +shared_ptr TypeGenerator::type() +{ + switch (typeCategory()) + { + case Type::INTEGER: + { + IntegerType::Bits b = static_cast( + state->uRandDist->distributionOneToN( + static_cast(IntegerType::Bits::B256) + ) + ); + // Choose signed/unsigned type with probability of 1/2 = 0.5 + bool signedType = state->uRandDist->probable(2); + return dynamic_pointer_cast(make_shared(b, signedType)); + } + case Type::BOOL: + return dynamic_pointer_cast(make_shared()); + case Type::FIXEDBYTES: + { + FixedBytesType::Bytes w = static_cast( + state->uRandDist->distributionOneToN( + static_cast(FixedBytesType::Bytes::W32) + ) + ); + return dynamic_pointer_cast(make_shared(w)); + } + case Type::BYTES: + return dynamic_pointer_cast(make_shared()); + case Type::ADDRESS: + return dynamic_pointer_cast(make_shared()); + case Type::FUNCTION: + return dynamic_pointer_cast(make_shared()); + case Type::CONTRACT: + if (state->sourceUnitState[state->currentPath()]->contractType()) + return state->sourceUnitState[state->currentPath()]->randomContractType(); + return dynamic_pointer_cast(make_shared()); + default: + solAssert(false, ""); + } } template diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h index 73846e64f..d0a8d3904 100644 --- a/test/tools/ossfuzz/SolidityGenerator.h +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -243,7 +243,7 @@ struct BytesType: SolidityType { std::string toString() override { - return "bytes"; + return "bytes memory"; } }; @@ -253,7 +253,7 @@ struct ContractType: SolidityType {} std::string toString() override { - return "type(" + contractName + ")"; + return name(); } std::string name() { @@ -265,13 +265,23 @@ struct ContractType: SolidityType struct FunctionType: SolidityType { FunctionType() = default; - ~FunctionType() + ~FunctionType() override { inputs.clear(); outputs.clear(); } - std::string toString() + void addInput(std::shared_ptr _input) + { + inputs.emplace_back(_input); + } + + void addOutput(std::shared_ptr _output) + { + outputs.emplace_back(_output); + } + + std::string toString() override { auto typeString = [](std::vector>& _types) { @@ -286,20 +296,20 @@ struct FunctionType: SolidityType return typeStr; }; + std::string retString = std::string("function ") + "(" + typeString(inputs) + ")"; if (outputs.empty()) - return std::string("function (") + typeString(inputs) + ") public pure {}"; + return retString + " public pure"; else - return std::string("function (") + - typeString(inputs) + - ") public pure returns (" + - typeString(outputs) + - ") {}"; + return retString + " public pure returns (" + typeString(outputs) + ")"; } std::vector> inputs; std::vector> outputs; }; +/// Forward declaration +struct TestState; + struct SourceState { explicit SourceState(std::shared_ptr _urd): @@ -308,7 +318,7 @@ struct SourceState {} void addFreeFunction(std::string& _functionName) { - exports[std::make_shared()] = _functionName; + exports[std::dynamic_pointer_cast(std::make_shared())] = _functionName; } bool freeFunction(std::string const& _functionName) { @@ -331,6 +341,18 @@ struct SourceState }) | ranges::to>; return contracts[uRandDist->distributionOneToN(contracts.size()) - 1]; } + std::shared_ptr randomContractType() + { + auto contracts = exports | + ranges::views::filter([](auto& _item) -> bool { + return bool(std::dynamic_pointer_cast(_item.first)); + }) | + ranges::views::transform([](auto& _item) -> std::shared_ptr { + return _item.first; + }) | + ranges::to>>; + return contracts[uRandDist->distributionOneToN(contracts.size()) - 1]; + } void addImportedSourcePath(std::string& _sourcePath) { importedSources.emplace(_sourcePath); @@ -355,7 +377,26 @@ struct SourceState std::map, std::string> exports; }; -struct FunctionState {}; +struct FunctionState +{ + FunctionState() = default; + ~FunctionState() + { + inputs.clear(); + outputs.clear(); + } + void addInput(std::pair> _input) + { + inputs.emplace(_input); + } + void addOutput(std::pair> _output) + { + outputs.emplace(_output); + } + + std::map> inputs; + std::map> outputs; +}; struct TestState { @@ -393,6 +434,10 @@ struct TestState functionState.emplace(_name, std::make_shared()); currentFunction = _name; } + std::shared_ptr currentFunctionState() + { + return functionState[currentFunction]; + } /// Returns true if @name sourceUnitPaths is empty, /// false otherwise. [[nodiscard]] bool empty() const @@ -502,6 +547,33 @@ struct TestState std::string const functionPrefix = "f"; }; +struct TypeGenerator +{ + TypeGenerator(std::shared_ptr _state): state(std::move(_state)) + {} + + enum class Type: size_t + { + INTEGER = 1, + BOOL, + FIXEDBYTES, + BYTES, + ADDRESS, + FUNCTION, + CONTRACT, + TYPEMAX + }; + + std::shared_ptr type(); + + Type typeCategory() + { + return static_cast(state->uRandDist->distributionOneToN(static_cast(Type::TYPEMAX) - 1)); + } + + std::shared_ptr state; +}; + struct GeneratorBase { explicit GeneratorBase(std::shared_ptr _mutator);