Merge pull request #2504 from ethereum/inlineasm-decl-empty

Support variable declarations without an assignment in assembly
This commit is contained in:
Alex Beregszaszi 2017-07-13 16:58:24 +02:00 committed by GitHub
commit b5da5f6e42
9 changed files with 68 additions and 16 deletions

View File

@ -2,6 +2,7 @@
Features: Features:
* Inline Assembly: Show useful error message if trying to access calldata variables. * Inline Assembly: Show useful error message if trying to access calldata variables.
* Inline Assembly: Support variable declaration without initial value (defaults to 0).
* Type Checker: Disallow value transfers to contracts without a payable fallback function * Type Checker: Disallow value transfers to contracts without a payable fallback function
Bugfixes: Bugfixes:

View File

@ -110,7 +110,7 @@ these curly braces, the following can be used (see the later sections for more d
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below - opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
- opcodes in functional style, e.g. ``add(1, mlod(0))`` - opcodes in functional style, e.g. ``add(1, mlod(0))``
- labels, e.g. ``name:`` - labels, e.g. ``name:``
- variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)`` - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned)
- identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add`` - identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add``
- assignments (in "instruction style"), e.g. ``3 =: x`` - assignments (in "instruction style"), e.g. ``3 =: x``
- assignments in functional style, e.g. ``x := add(y, 3)`` - assignments in functional style, e.g. ``x := add(y, 3)``

View File

@ -37,10 +37,19 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
{ {
solAssert(m_scope, ""); solAssert(m_scope, "");
int expectedItems = _varDecl.variables.size(); int const numVariables = _varDecl.variables.size();
int height = m_assembly.stackHeight(); int height = m_assembly.stackHeight();
boost::apply_visitor(*this, *_varDecl.value); if (_varDecl.value)
expectDeposit(expectedItems, height); {
boost::apply_visitor(*this, *_varDecl.value);
expectDeposit(numVariables, height);
}
else
{
int variablesLeft = numVariables;
while (variablesLeft--)
m_assembly.appendConstant(u256(0));
}
for (auto const& variable: _varDecl.variables) for (auto const& variable: _varDecl.variables)
{ {
auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name)); auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name));

View File

@ -174,14 +174,20 @@ bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{ {
int const expectedItems = _varDecl.variables.size(); bool success = true;
int const stackHeight = m_stackHeight; int const numVariables = _varDecl.variables.size();
bool success = boost::apply_visitor(*this, *_varDecl.value); if (_varDecl.value)
if ((m_stackHeight - stackHeight) != expectedItems)
{ {
m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch."); int const stackHeight = m_stackHeight;
return false; success = boost::apply_visitor(*this, *_varDecl.value);
if ((m_stackHeight - stackHeight) != numVariables)
{
m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
return false;
}
} }
else
m_stackHeight += numVariables;
for (auto const& variable: _varDecl.variables) for (auto const& variable: _varDecl.variables)
{ {

View File

@ -347,10 +347,15 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
else else
break; break;
} }
expectToken(Token::Colon); if (currentToken() == Token::Colon)
expectToken(Token::Assign); {
varDecl.value.reset(new Statement(parseExpression())); expectToken(Token::Colon);
varDecl.location.end = locationOf(*varDecl.value).end; expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression()));
varDecl.location.end = locationOf(*varDecl.value).end;
}
else
varDecl.location.end = varDecl.variables.back().location.end;
return varDecl; return varDecl;
} }

View File

@ -128,8 +128,11 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl
), ),
", " ", "
); );
out += " := "; if (_variableDeclaration.value)
out += boost::apply_visitor(*this, *_variableDeclaration.value); {
out += " := ";
out += boost::apply_visitor(*this, *_variableDeclaration.value);
}
return out; return out;
} }

View File

@ -131,6 +131,11 @@ BOOST_AUTO_TEST_CASE(vardecl_bool)
BOOST_CHECK(successParse("{ let x:bool := false:bool }")); BOOST_CHECK(successParse("{ let x:bool := false:bool }"));
} }
BOOST_AUTO_TEST_CASE(vardecl_empty)
{
BOOST_CHECK(successParse("{ let x:u256 }"));
}
BOOST_AUTO_TEST_CASE(assignment) BOOST_AUTO_TEST_CASE(assignment)
{ {
BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }"));

View File

@ -195,6 +195,11 @@ BOOST_AUTO_TEST_CASE(vardecl_bool)
CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals."); CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals.");
} }
BOOST_AUTO_TEST_CASE(vardecl_empty)
{
BOOST_CHECK(successParse("{ let x }"));
}
BOOST_AUTO_TEST_CASE(assignment) BOOST_AUTO_TEST_CASE(assignment)
{ {
BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }")); BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));

View File

@ -9723,6 +9723,24 @@ BOOST_AUTO_TEST_CASE(multi_modifiers)
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(12))); BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(12)));
} }
BOOST_AUTO_TEST_CASE(inlineasm_empty_let)
{
char const* sourceCode = R"(
contract C {
function f() returns (uint a, uint b) {
assembly {
let x
let y, z
a := x
b := z
}
}
}
)";
compileAndRun(sourceCode, 0, "C");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0), u256(0)));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }