diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 6fe9c2452..95a42f49f 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -90,12 +90,33 @@ string ProtoConverter::visit(Literal const& _x) } } +void ProtoConverter::consolidateVarDeclsInFunctionDef() +{ + m_currentFuncVars.clear(); + auto &scopes = m_funcVars.back(); + for (auto &s: scopes) + m_currentFuncVars.insert(m_currentFuncVars.end(), s.begin(), s.end()); +} + +void ProtoConverter::consolidateGlobalVarDecls() +{ + m_globalVars.clear(); + for (auto &scope: m_variables) + m_globalVars.insert(m_globalVars.end(), scope.begin(), scope.end()); +} + bool ProtoConverter::varDeclAvailable() { if (m_inFunctionDef) - return m_scopeVars.top().size() > 0; + { + consolidateVarDeclsInFunctionDef(); + return m_currentFuncVars.size() > 0; + } else - return m_variables.size() > 0; + { + consolidateGlobalVarDecls(); + return m_globalVars.size() > 0; + } } bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type) @@ -109,14 +130,14 @@ void ProtoConverter::visit(VarRef const& _x) if (m_inFunctionDef) { // Ensure that there is at least one variable declaration to reference in function scope. - yulAssert(m_scopeVars.top().size() > 0, "Proto fuzzer: No variables to reference."); - m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()]; + yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference."); + m_output << m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()]; } else { // Ensure that there is at least one variable declaration to reference in nested scopes. - yulAssert(m_variables.size() > 0, "Proto fuzzer: No variables to reference."); - m_output << m_variables[_x.varnum() % m_variables.size()]; + yulAssert(m_globalVars.size() > 0, "Proto fuzzer: No global variables to reference."); + m_output << m_globalVars[_x.varnum() % m_globalVars.size()]; } } @@ -258,8 +279,10 @@ void ProtoConverter::visit(VarDecl const& _x) m_output << "let " << varName << " := "; visit(_x.expr()); m_output << "\n"; - m_scopeVars.top().push_back(varName); - m_variables.push_back(varName); + if (m_inFunctionDef) + m_funcVars.back().back().push_back(varName); + else + m_variables.back().push_back(varName); } void ProtoConverter::visit(TypedVarDecl const& _x) @@ -324,8 +347,10 @@ void ProtoConverter::visit(TypedVarDecl const& _x) m_output << " : u256\n"; break; } - m_scopeVars.top().push_back(varName); - m_variables.push_back(varName); + if (m_inFunctionDef) + m_funcVars.back().back().push_back(varName); + else + m_variables.back().push_back(varName); } void ProtoConverter::visit(UnaryOp const& _x) @@ -1102,12 +1127,19 @@ void ProtoConverter::visit(Statement const& _x) } } -void ProtoConverter::openScope(vector const& _funcParams) +void ProtoConverter::openBlockScope() { - m_scopeVars.push({}); - m_scopeFuncs.push({}); - if (!_funcParams.empty()) - addVarsToScope(_funcParams); + m_scopeFuncs.push_back({}); + // Create new block scope inside current function scope + if (m_inFunctionDef) + m_funcVars.back().push_back(vector{}); + else + m_variables.push_back(vector{}); +} + +void ProtoConverter::openFunctionScope(vector const& _funcParams) +{ + m_funcVars.push_back(vector>({_funcParams})); } void ProtoConverter::updateFunctionMaps(string const& _var) @@ -1124,21 +1156,9 @@ void ProtoConverter::updateFunctionMaps(string const& _var) yulAssert(erased == 2, "Proto fuzzer: Function maps not updated"); } -void ProtoConverter::closeScope() +void ProtoConverter::closeBlockScope() { - for (auto const& var: m_scopeVars.top()) - { - unsigned numVarsRemoved = m_variables.size(); - m_variables.erase(remove(m_variables.begin(), m_variables.end(), var), m_variables.end()); - numVarsRemoved -= m_variables.size(); - yulAssert( - numVarsRemoved == 1, - "Proto fuzzer: Nothing or too much went out of scope" - ); - } - m_scopeVars.pop(); - - for (auto const& f: m_scopeFuncs.top()) + for (auto const& f: m_scopeFuncs.back()) { unsigned numFuncsRemoved = m_functions.size(); m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end()); @@ -1149,21 +1169,40 @@ void ProtoConverter::closeScope() ); updateFunctionMaps(f); } - m_scopeFuncs.pop(); + if (!m_scopeFuncs.empty()) + m_scopeFuncs.pop_back(); + if (!m_inFunctionDef) + { + if (!m_variables.empty()) + m_variables.pop_back(); + } + else + { + // Variables that have been declared in a + // function block, go out of scope + if (!m_funcVars.empty()) + if (!m_funcVars.back().empty()) + m_funcVars.back().pop_back(); + } +} + +void ProtoConverter::closeFunctionScope() +{ + if (!m_funcVars.empty()) + m_funcVars.pop_back(); } void ProtoConverter::addVarsToScope(vector const& _vars) { - for (string const& i: _vars) - { - m_variables.push_back(i); - m_scopeVars.top().push_back(i); - } + if (m_inFunctionDef) + m_funcVars.back().back().insert(m_funcVars.back().back().end(), _vars.begin(), _vars.end()); + else + m_variables.back().insert(m_variables.back().end(), _vars.begin(), _vars.end()); } -void ProtoConverter::visit(Block const& _x, vector _funcParams) +void ProtoConverter::visit(Block const& _x) { - openScope(_funcParams); + openBlockScope(); // Register function declarations in this scope unless this // scope belongs to for-init (in which function declarations @@ -1181,7 +1220,7 @@ void ProtoConverter::visit(Block const& _x, vector _funcParams) } else m_output << "{}\n"; - closeScope(); + closeBlockScope(); } vector ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx) @@ -1224,7 +1263,7 @@ void ProtoConverter::registerFunction(FunctionDef const* _x) auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams))); yulAssert(ret.second, "Proto fuzzer: Function already exists."); m_functions.push_back(funcName); - m_scopeFuncs.top().push_back(funcName); + m_scopeFuncs.back().push_back(funcName); m_functionDefMap.emplace(make_pair(_x, funcName)); } @@ -1363,8 +1402,12 @@ void ProtoConverter::createFunctionDefAndCall( bool wasInFunctionDef = m_inFunctionDef; m_inFunctionDef = true; - // Body - visit(_x.block(), varsVec); + // Create new function scope and add function input and return + // parameters to it. + openFunctionScope(varsVec); + // Visit function body + visit(_x.block()); + closeFunctionScope(); m_inForBodyScope = wasInForBody; m_inFunctionDef = wasInFunctionDef; diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index ef149d198..6823a2925 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -42,6 +42,8 @@ class ProtoConverter public: ProtoConverter() { + m_funcVars = std::vector>>{}; + m_variables = std::vector>{}; m_inForBodyScope = false; m_inForInitScope = false; m_numNestedForLoops = 0; @@ -62,7 +64,7 @@ private: /// @param _block Reference to a basic block of yul statements. /// @param _funcParams List of function parameter names, defaults to /// an empty vector. - void visit(Block const& _block, std::vector _funcParams = {}); + void visit(Block const& _block); std::string visit(Literal const&); void visit(VarRef const&); @@ -98,11 +100,15 @@ private: void visit(Code const&); void visit(Program const&); - /// Creates a new scope, and adds @a _funcParams to it if it + /// Creates a new block scope. + void openBlockScope(); + /// Creates a new function scope, and adds @a _funcParams to it if it /// is non-empty. - void openScope(std::vector const& _funcParams); - /// Closes current scope - void closeScope(); + void openFunctionScope(std::vector const& _funcParams); + /// Closes current block scope + void closeBlockScope(); + /// Closes current function scope + void closeFunctionScope(); /// Adds @a _vars to current scope void addVarsToScope(std::vector const& _vars); @@ -138,6 +144,14 @@ private: /// Multiple -> "m" std::string functionTypeToString(NumFunctionReturns _type); + /// Builds a single vector containing variables declared in + /// function scope. + void consolidateVarDeclsInFunctionDef(); + + /// Builds a single vector containing variables declared in + /// global scope. + void consolidateGlobalVarDecls(); + /// Return true if at least one variable declaration is in scope, /// false otherwise. /// @return True in the following cases: @@ -295,12 +309,16 @@ private: } std::ostringstream m_output; - /// Variables in current scope - std::stack> m_scopeVars; + /// Variables in all function definitions + std::vector>> m_funcVars; + /// Variables in current function definition + std::vector m_currentFuncVars; + /// Variables in global scope + std::vector m_globalVars; /// Functions in current scope - std::stack> m_scopeFuncs; + std::vector> m_scopeFuncs; /// Variables - std::vector m_variables; + std::vector> m_variables; /// Functions std::vector m_functions; /// Maps FunctionDef object to its name diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 57441e942..155973e1f 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -55,8 +55,7 @@ void printErrors(ostream& _stream, ErrorList const& _errors) DEFINE_PROTO_FUZZER(Program const& _input) { - ProtoConverter converter; - string yul_source = converter.programToString(_input); + string yul_source = ProtoConverter().programToString(_input); if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) { @@ -82,7 +81,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) !stack.parserResult()->analysisInfo) { printErrors(std::cout, stack.errors()); - return; + yulAssert(false, "Proto fuzzer generated malformed program"); } } catch (Exception const&)