mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
yul proto fuzzer: Generalize variable references
This commit is contained in:
parent
95eafba7d9
commit
41bdc9b673
@ -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()
|
bool ProtoConverter::varDeclAvailable()
|
||||||
{
|
{
|
||||||
if (m_inFunctionDef)
|
if (m_inFunctionDef)
|
||||||
return m_scopeVars.top().size() > 0;
|
{
|
||||||
|
consolidateVarDeclsInFunctionDef();
|
||||||
|
return m_currentFuncVars.size() > 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return m_variables.size() > 0;
|
{
|
||||||
|
consolidateGlobalVarDecls();
|
||||||
|
return m_globalVars.size() > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
|
bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
|
||||||
@ -109,14 +130,14 @@ void ProtoConverter::visit(VarRef const& _x)
|
|||||||
if (m_inFunctionDef)
|
if (m_inFunctionDef)
|
||||||
{
|
{
|
||||||
// Ensure that there is at least one variable declaration to reference in function scope.
|
// 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.");
|
yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
|
||||||
m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()];
|
m_output << m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Ensure that there is at least one variable declaration to reference in nested scopes.
|
// 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.");
|
yulAssert(m_globalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
|
||||||
m_output << m_variables[_x.varnum() % m_variables.size()];
|
m_output << m_globalVars[_x.varnum() % m_globalVars.size()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,8 +279,10 @@ void ProtoConverter::visit(VarDecl const& _x)
|
|||||||
m_output << "let " << varName << " := ";
|
m_output << "let " << varName << " := ";
|
||||||
visit(_x.expr());
|
visit(_x.expr());
|
||||||
m_output << "\n";
|
m_output << "\n";
|
||||||
m_scopeVars.top().push_back(varName);
|
if (m_inFunctionDef)
|
||||||
m_variables.push_back(varName);
|
m_funcVars.back().back().push_back(varName);
|
||||||
|
else
|
||||||
|
m_variables.back().push_back(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(TypedVarDecl const& _x)
|
void ProtoConverter::visit(TypedVarDecl const& _x)
|
||||||
@ -324,8 +347,10 @@ void ProtoConverter::visit(TypedVarDecl const& _x)
|
|||||||
m_output << " : u256\n";
|
m_output << " : u256\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_scopeVars.top().push_back(varName);
|
if (m_inFunctionDef)
|
||||||
m_variables.push_back(varName);
|
m_funcVars.back().back().push_back(varName);
|
||||||
|
else
|
||||||
|
m_variables.back().push_back(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(UnaryOp const& _x)
|
void ProtoConverter::visit(UnaryOp const& _x)
|
||||||
@ -1102,12 +1127,19 @@ void ProtoConverter::visit(Statement const& _x)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::openScope(vector<string> const& _funcParams)
|
void ProtoConverter::openBlockScope()
|
||||||
{
|
{
|
||||||
m_scopeVars.push({});
|
m_scopeFuncs.push_back({});
|
||||||
m_scopeFuncs.push({});
|
// Create new block scope inside current function scope
|
||||||
if (!_funcParams.empty())
|
if (m_inFunctionDef)
|
||||||
addVarsToScope(_funcParams);
|
m_funcVars.back().push_back(vector<string>{});
|
||||||
|
else
|
||||||
|
m_variables.push_back(vector<string>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtoConverter::openFunctionScope(vector<string> const& _funcParams)
|
||||||
|
{
|
||||||
|
m_funcVars.push_back(vector<vector<string>>({_funcParams}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::updateFunctionMaps(string const& _var)
|
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");
|
yulAssert(erased == 2, "Proto fuzzer: Function maps not updated");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::closeScope()
|
void ProtoConverter::closeBlockScope()
|
||||||
{
|
{
|
||||||
for (auto const& var: m_scopeVars.top())
|
for (auto const& f: m_scopeFuncs.back())
|
||||||
{
|
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
unsigned numFuncsRemoved = m_functions.size();
|
unsigned numFuncsRemoved = m_functions.size();
|
||||||
m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
|
m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
|
||||||
@ -1149,21 +1169,40 @@ void ProtoConverter::closeScope()
|
|||||||
);
|
);
|
||||||
updateFunctionMaps(f);
|
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<string> const& _vars)
|
void ProtoConverter::addVarsToScope(vector<string> const& _vars)
|
||||||
{
|
{
|
||||||
for (string const& i: _vars)
|
if (m_inFunctionDef)
|
||||||
{
|
m_funcVars.back().back().insert(m_funcVars.back().back().end(), _vars.begin(), _vars.end());
|
||||||
m_variables.push_back(i);
|
else
|
||||||
m_scopeVars.top().push_back(i);
|
m_variables.back().insert(m_variables.back().end(), _vars.begin(), _vars.end());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtoConverter::visit(Block const& _x, vector<string> _funcParams)
|
void ProtoConverter::visit(Block const& _x)
|
||||||
{
|
{
|
||||||
openScope(_funcParams);
|
openBlockScope();
|
||||||
|
|
||||||
// Register function declarations in this scope unless this
|
// Register function declarations in this scope unless this
|
||||||
// scope belongs to for-init (in which function declarations
|
// scope belongs to for-init (in which function declarations
|
||||||
@ -1181,7 +1220,7 @@ void ProtoConverter::visit(Block const& _x, vector<string> _funcParams)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_output << "{}\n";
|
m_output << "{}\n";
|
||||||
closeScope();
|
closeBlockScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx)
|
vector<string> 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)));
|
auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams)));
|
||||||
yulAssert(ret.second, "Proto fuzzer: Function already exists.");
|
yulAssert(ret.second, "Proto fuzzer: Function already exists.");
|
||||||
m_functions.push_back(funcName);
|
m_functions.push_back(funcName);
|
||||||
m_scopeFuncs.top().push_back(funcName);
|
m_scopeFuncs.back().push_back(funcName);
|
||||||
m_functionDefMap.emplace(make_pair(_x, funcName));
|
m_functionDefMap.emplace(make_pair(_x, funcName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1363,8 +1402,12 @@ void ProtoConverter::createFunctionDefAndCall(
|
|||||||
bool wasInFunctionDef = m_inFunctionDef;
|
bool wasInFunctionDef = m_inFunctionDef;
|
||||||
m_inFunctionDef = true;
|
m_inFunctionDef = true;
|
||||||
|
|
||||||
// Body
|
// Create new function scope and add function input and return
|
||||||
visit(_x.block(), varsVec);
|
// parameters to it.
|
||||||
|
openFunctionScope(varsVec);
|
||||||
|
// Visit function body
|
||||||
|
visit(_x.block());
|
||||||
|
closeFunctionScope();
|
||||||
|
|
||||||
m_inForBodyScope = wasInForBody;
|
m_inForBodyScope = wasInForBody;
|
||||||
m_inFunctionDef = wasInFunctionDef;
|
m_inFunctionDef = wasInFunctionDef;
|
||||||
|
@ -42,6 +42,8 @@ class ProtoConverter
|
|||||||
public:
|
public:
|
||||||
ProtoConverter()
|
ProtoConverter()
|
||||||
{
|
{
|
||||||
|
m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
|
||||||
|
m_variables = std::vector<std::vector<std::string>>{};
|
||||||
m_inForBodyScope = false;
|
m_inForBodyScope = false;
|
||||||
m_inForInitScope = false;
|
m_inForInitScope = false;
|
||||||
m_numNestedForLoops = 0;
|
m_numNestedForLoops = 0;
|
||||||
@ -62,7 +64,7 @@ private:
|
|||||||
/// @param _block Reference to a basic block of yul statements.
|
/// @param _block Reference to a basic block of yul statements.
|
||||||
/// @param _funcParams List of function parameter names, defaults to
|
/// @param _funcParams List of function parameter names, defaults to
|
||||||
/// an empty vector.
|
/// an empty vector.
|
||||||
void visit(Block const& _block, std::vector<std::string> _funcParams = {});
|
void visit(Block const& _block);
|
||||||
|
|
||||||
std::string visit(Literal const&);
|
std::string visit(Literal const&);
|
||||||
void visit(VarRef const&);
|
void visit(VarRef const&);
|
||||||
@ -98,11 +100,15 @@ private:
|
|||||||
void visit(Code const&);
|
void visit(Code const&);
|
||||||
void visit(Program 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.
|
/// is non-empty.
|
||||||
void openScope(std::vector<std::string> const& _funcParams);
|
void openFunctionScope(std::vector<std::string> const& _funcParams);
|
||||||
/// Closes current scope
|
/// Closes current block scope
|
||||||
void closeScope();
|
void closeBlockScope();
|
||||||
|
/// Closes current function scope
|
||||||
|
void closeFunctionScope();
|
||||||
/// Adds @a _vars to current scope
|
/// Adds @a _vars to current scope
|
||||||
void addVarsToScope(std::vector<std::string> const& _vars);
|
void addVarsToScope(std::vector<std::string> const& _vars);
|
||||||
|
|
||||||
@ -138,6 +144,14 @@ private:
|
|||||||
/// Multiple -> "m"
|
/// Multiple -> "m"
|
||||||
std::string functionTypeToString(NumFunctionReturns _type);
|
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,
|
/// Return true if at least one variable declaration is in scope,
|
||||||
/// false otherwise.
|
/// false otherwise.
|
||||||
/// @return True in the following cases:
|
/// @return True in the following cases:
|
||||||
@ -295,12 +309,16 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream m_output;
|
std::ostringstream m_output;
|
||||||
/// Variables in current scope
|
/// Variables in all function definitions
|
||||||
std::stack<std::vector<std::string>> m_scopeVars;
|
std::vector<std::vector<std::vector<std::string>>> m_funcVars;
|
||||||
|
/// Variables in current function definition
|
||||||
|
std::vector<std::string> m_currentFuncVars;
|
||||||
|
/// Variables in global scope
|
||||||
|
std::vector<std::string> m_globalVars;
|
||||||
/// Functions in current scope
|
/// Functions in current scope
|
||||||
std::stack<std::vector<std::string>> m_scopeFuncs;
|
std::vector<std::vector<std::string>> m_scopeFuncs;
|
||||||
/// Variables
|
/// Variables
|
||||||
std::vector<std::string> m_variables;
|
std::vector<std::vector<std::string>> m_variables;
|
||||||
/// Functions
|
/// Functions
|
||||||
std::vector<std::string> m_functions;
|
std::vector<std::string> m_functions;
|
||||||
/// Maps FunctionDef object to its name
|
/// Maps FunctionDef object to its name
|
||||||
|
@ -55,8 +55,7 @@ void printErrors(ostream& _stream, ErrorList const& _errors)
|
|||||||
|
|
||||||
DEFINE_PROTO_FUZZER(Program const& _input)
|
DEFINE_PROTO_FUZZER(Program const& _input)
|
||||||
{
|
{
|
||||||
ProtoConverter converter;
|
string yul_source = ProtoConverter().programToString(_input);
|
||||||
string yul_source = converter.programToString(_input);
|
|
||||||
|
|
||||||
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
||||||
{
|
{
|
||||||
@ -82,7 +81,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
!stack.parserResult()->analysisInfo)
|
!stack.parserResult()->analysisInfo)
|
||||||
{
|
{
|
||||||
printErrors(std::cout, stack.errors());
|
printErrors(std::cout, stack.errors());
|
||||||
return;
|
yulAssert(false, "Proto fuzzer generated malformed program");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception const&)
|
catch (Exception const&)
|
||||||
|
Loading…
Reference in New Issue
Block a user