Fix handling of scopes in Yul Interpreter.

This commit is contained in:
chriseth 2019-08-28 16:18:05 +02:00
parent 459aed90e0
commit 01e0a12c3b
4 changed files with 88 additions and 48 deletions

View File

@ -0,0 +1,20 @@
{
f(1)
function f(i) {
if i { g(1) }
function g(j) {
if j { h() }
f(0)
function h() {
g(0)
}
}
sstore(i, add(i, 7))
}
}
// ----
// Trace:
// Memory dump:
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007
// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000008

View File

@ -0,0 +1,14 @@
{
function fib(i) -> y {
y := 1
if gt(i, 2) {
y := add(fib(sub(i, 1)), fib(sub(i, 2)))
}
}
sstore(0, fib(8))
}
// ----
// Trace:
// Memory dump:
// Storage dump:
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000015

View File

@ -90,7 +90,8 @@ void Interpreter::operator()(VariableDeclaration const& _declaration)
YulString varName = _declaration.variables.at(i).name; YulString varName = _declaration.variables.at(i).name;
solAssert(!m_variables.count(varName), ""); solAssert(!m_variables.count(varName), "");
m_variables[varName] = values.at(i); m_variables[varName] = values.at(i);
m_scopes.back().insert(varName); solAssert(!m_scopes.back().count(varName), "");
m_scopes.back().emplace(varName, nullptr);
} }
} }
@ -164,8 +165,8 @@ void Interpreter::operator()(Block const& _block)
if (statement.type() == typeid(FunctionDefinition)) if (statement.type() == typeid(FunctionDefinition))
{ {
FunctionDefinition const& funDef = boost::get<FunctionDefinition>(statement); FunctionDefinition const& funDef = boost::get<FunctionDefinition>(statement);
m_functions[funDef.name] = &funDef; solAssert(!m_scopes.back().count(funDef.name), "");
m_scopes.back().insert(funDef.name); m_scopes.back().emplace(funDef.name, &funDef);
} }
for (auto const& statement: _block.statements) for (auto const& statement: _block.statements)
@ -180,25 +181,23 @@ void Interpreter::operator()(Block const& _block)
u256 Interpreter::evaluate(Expression const& _expression) u256 Interpreter::evaluate(Expression const& _expression)
{ {
ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_functions, m_scopes); ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_scopes);
ev.visit(_expression); ev.visit(_expression);
return ev.value(); return ev.value();
} }
vector<u256> Interpreter::evaluateMulti(Expression const& _expression) vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
{ {
ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_functions, m_scopes); ExpressionEvaluator ev(m_state, m_dialect, m_variables, m_scopes);
ev.visit(_expression); ev.visit(_expression);
return ev.values(); return ev.values();
} }
void Interpreter::closeScope() void Interpreter::closeScope()
{ {
for (auto const& var: m_scopes.back()) for (auto const& [var, funDeclaration]: m_scopes.back())
{ if (!funDeclaration)
size_t erased = m_variables.erase(var) + m_functions.erase(var); solAssert(m_variables.erase(var) == 1, "");
solAssert(erased == 1, "");
}
m_scopes.pop_back(); m_scopes.pop_back();
} }
@ -237,22 +236,21 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
return; return;
} }
solAssert(m_functions.count(_funCall.functionName.name), ""); auto [functionScopes, fun] = findFunctionAndScope(_funCall.functionName.name);
FunctionDefinition const& fun = *m_functions.at(_funCall.functionName.name);
solAssert(m_values.size() == fun.parameters.size(), "");
map<YulString, u256> variables;
for (size_t i = 0; i < fun.parameters.size(); ++i)
variables[fun.parameters.at(i).name] = m_values.at(i);
for (size_t i = 0; i < fun.returnVariables.size(); ++i)
variables[fun.returnVariables.at(i).name] = 0;
// TODO function name lookup could be a little more efficient, solAssert(fun, "Function not found.");
// we have to copy the list here. solAssert(m_values.size() == fun->parameters.size(), "");
Interpreter interpreter(m_state, m_dialect, variables, visibleFunctionsFor(fun.name)); map<YulString, u256> variables;
interpreter(fun.body); for (size_t i = 0; i < fun->parameters.size(); ++i)
variables[fun->parameters.at(i).name] = m_values.at(i);
for (size_t i = 0; i < fun->returnVariables.size(); ++i)
variables[fun->returnVariables.at(i).name] = 0;
Interpreter interpreter(m_state, m_dialect, variables, functionScopes);
interpreter(fun->body);
m_values.clear(); m_values.clear();
for (auto const& retVar: fun.returnVariables) for (auto const& retVar: fun->returnVariables)
m_values.emplace_back(interpreter.valueOfVariable(retVar.name)); m_values.emplace_back(interpreter.valueOfVariable(retVar.name));
} }
@ -281,19 +279,26 @@ void ExpressionEvaluator::evaluateArgs(vector<Expression> const& _expr)
std::reverse(m_values.begin(), m_values.end()); std::reverse(m_values.begin(), m_values.end());
} }
std::map<YulString, FunctionDefinition const*> ExpressionEvaluator::visibleFunctionsFor(YulString const& _name) pair<
vector<map<YulString, FunctionDefinition const*>>,
FunctionDefinition const*
> ExpressionEvaluator::findFunctionAndScope(YulString _functionName) const
{ {
std::map<YulString, FunctionDefinition const*> functions; FunctionDefinition const* fun = nullptr;
std::vector<std::map<YulString, FunctionDefinition const*>> newScopes;
for (auto const& scope: m_scopes) for (auto const& scope: m_scopes)
{ {
for (auto const& symbol: scope) // Copy over all functions.
if (m_functions.count(symbol) > 0) newScopes.push_back({});
functions[symbol] = m_functions.at(symbol); for (auto const& [name, funDef]: scope)
if (funDef)
if (scope.count(_name)) newScopes.back().emplace(name, funDef);
// Stop at the called function.
if (scope.count(_functionName))
{
fun = scope.at(_functionName);
break; break;
}
} }
return {move(newScopes), fun};
return functions;
} }

View File

@ -106,12 +106,12 @@ public:
InterpreterState& _state, InterpreterState& _state,
Dialect const& _dialect, Dialect const& _dialect,
std::map<YulString, dev::u256> _variables = {}, std::map<YulString, dev::u256> _variables = {},
std::map<YulString, FunctionDefinition const*> _functions = {} std::vector<std::map<YulString, FunctionDefinition const*>> _scopes = {}
): ):
m_dialect(_dialect), m_dialect(_dialect),
m_state(_state), m_state(_state),
m_variables(std::move(_variables)), m_variables(std::move(_variables)),
m_functions(std::move(_functions)) m_scopes(std::move(_scopes))
{} {}
void operator()(ExpressionStatement const& _statement) override; void operator()(ExpressionStatement const& _statement) override;
@ -136,17 +136,17 @@ private:
std::vector<dev::u256> evaluateMulti(Expression const& _expression); std::vector<dev::u256> evaluateMulti(Expression const& _expression);
void openScope() { m_scopes.push_back({}); } void openScope() { m_scopes.push_back({}); }
/// Unregisters variables. /// Unregisters variables and functions.
void closeScope(); void closeScope();
Dialect const& m_dialect; Dialect const& m_dialect;
InterpreterState& m_state; InterpreterState& m_state;
/// Values of variables. /// Values of variables.
std::map<YulString, dev::u256> m_variables; std::map<YulString, dev::u256> m_variables;
/// Meanings of functions. /// Scopes of variables and functions. Used for lookup, clearing at end of blocks
std::map<YulString, FunctionDefinition const*> m_functions; /// and passing over the visible functions across function calls.
/// Scopes of variables and functions, used to clear them at end of blocks. /// The pointer is nullptr if and only if the key is a variable.
std::vector<std::set<YulString>> m_scopes; std::vector<std::map<YulString, FunctionDefinition const*>> m_scopes;
}; };
/** /**
@ -159,13 +159,11 @@ public:
InterpreterState& _state, InterpreterState& _state,
Dialect const& _dialect, Dialect const& _dialect,
std::map<YulString, dev::u256> const& _variables, std::map<YulString, dev::u256> const& _variables,
std::map<YulString, FunctionDefinition const*> const& _functions, std::vector<std::map<YulString, FunctionDefinition const*>> const& _scopes
std::vector<std::set<YulString>> const& _scopes
): ):
m_state(_state), m_state(_state),
m_dialect(_dialect), m_dialect(_dialect),
m_variables(_variables), m_variables(_variables),
m_functions(_functions),
m_scopes(_scopes) m_scopes(_scopes)
{} {}
@ -186,16 +184,19 @@ private:
/// stores it in m_value. /// stores it in m_value.
void evaluateArgs(std::vector<Expression> const& _expr); void evaluateArgs(std::vector<Expression> const& _expr);
/// Extracts functions from the earlier scopes that are visible for the given function /// Finds the function called @a _functionName in the current scope stack and returns
std::map<YulString, FunctionDefinition const*> visibleFunctionsFor(YulString const& _name); /// the function's scope stack (with variables removed) and definition.
std::pair<
std::vector<std::map<YulString, FunctionDefinition const*>>,
FunctionDefinition const*
> findFunctionAndScope(YulString _functionName) const;
InterpreterState& m_state; InterpreterState& m_state;
Dialect const& m_dialect; Dialect const& m_dialect;
/// Values of variables. /// Values of variables.
std::map<YulString, dev::u256> const& m_variables; std::map<YulString, dev::u256> const& m_variables;
/// Meanings of functions. /// Stack of scopes in the current context.
std::map<YulString, FunctionDefinition const*> const& m_functions; std::vector<std::map<YulString, FunctionDefinition const*>> const& m_scopes;
std::vector<std::set<YulString>> const& m_scopes;
/// Current value of the expression /// Current value of the expression
std::vector<dev::u256> m_values; std::vector<dev::u256> m_values;
}; };