mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Yul] Adds break/continue statements and some general tests for for-loop syntax.
This commit is contained in:
parent
4704ef843d
commit
05e2d362c8
@ -94,6 +94,12 @@ public:
|
||||
(*this)(_for.body);
|
||||
(*this)(_for.post);
|
||||
}
|
||||
void operator()(yul::Break const&)
|
||||
{
|
||||
}
|
||||
void operator()(yul::Continue const&)
|
||||
{
|
||||
}
|
||||
void operator()(yul::Block const& _block)
|
||||
{
|
||||
for (auto const& s: _block.statements)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <libyul/AsmScope.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/Utilities.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@ -33,6 +34,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -472,7 +474,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
||||
{
|
||||
solAssert(_for.condition, "");
|
||||
|
||||
Scope* originalScope = m_currentScope;
|
||||
Scope* outerScope = m_currentScope;
|
||||
|
||||
bool success = true;
|
||||
if (!(*this)(_for.pre))
|
||||
@ -485,18 +487,37 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
||||
if (!expectExpression(*_for.condition))
|
||||
success = false;
|
||||
m_stackHeight--;
|
||||
|
||||
// backup outer for-loop & create new state
|
||||
auto outerForLoop = m_currentForLoop;
|
||||
m_currentForLoop = &_for;
|
||||
|
||||
if (!(*this)(_for.body))
|
||||
success = false;
|
||||
|
||||
if (!(*this)(_for.post))
|
||||
success = false;
|
||||
|
||||
m_stackHeight -= scope(&_for.pre).numberOfVariables();
|
||||
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
||||
m_currentScope = originalScope;
|
||||
m_currentScope = outerScope;
|
||||
m_currentForLoop = outerForLoop;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Break const& _break)
|
||||
{
|
||||
m_info.stackHeightInfo[&_break] = m_stackHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Continue const& _continue)
|
||||
{
|
||||
m_info.stackHeightInfo[&_continue] = m_stackHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::operator()(Block const& _block)
|
||||
{
|
||||
bool success = true;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
namespace langutil
|
||||
@ -93,6 +94,8 @@ public:
|
||||
bool operator()(If const& _if);
|
||||
bool operator()(Switch const& _switch);
|
||||
bool operator()(ForLoop const& _forLoop);
|
||||
bool operator()(Break const&);
|
||||
bool operator()(Continue const&);
|
||||
bool operator()(Block const& _block);
|
||||
|
||||
private:
|
||||
@ -124,6 +127,7 @@ private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
std::shared_ptr<Dialect> m_dialect;
|
||||
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
||||
ForLoop const* m_currentForLoop = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -78,6 +78,10 @@ struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value;
|
||||
/// Switch statement
|
||||
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
|
||||
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
|
||||
/// Break statement (valid within for loop)
|
||||
struct Break { langutil::SourceLocation location; };
|
||||
/// Continue statement (valid within for loop)
|
||||
struct Continue { langutil::SourceLocation location; };
|
||||
|
||||
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
||||
{
|
||||
|
@ -41,12 +41,14 @@ struct If;
|
||||
struct Switch;
|
||||
struct Case;
|
||||
struct ForLoop;
|
||||
struct Break;
|
||||
struct Continue;
|
||||
struct ExpressionStatement;
|
||||
struct Block;
|
||||
|
||||
struct TypedName;
|
||||
|
||||
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
||||
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
|
||||
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@ -104,6 +105,32 @@ Statement Parser::parseStatement()
|
||||
}
|
||||
case Token::For:
|
||||
return parseForLoop();
|
||||
case Token::Break:
|
||||
if (m_insideForLoopBody)
|
||||
{
|
||||
auto stmt = Statement{ createWithLocation<Break>() };
|
||||
m_scanner->next();
|
||||
return stmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorReporter.syntaxError(location(), "Keyword break outside for-loop body is not allowed.");
|
||||
m_scanner->next();
|
||||
return {};
|
||||
}
|
||||
case Token::Continue:
|
||||
if (m_insideForLoopBody)
|
||||
{
|
||||
auto stmt = Statement{ createWithLocation<Continue>() };
|
||||
m_scanner->next();
|
||||
return stmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorReporter.syntaxError(location(), "Keyword continue outside for-loop body is not allowed.");
|
||||
m_scanner->next();
|
||||
return {};
|
||||
}
|
||||
case Token::Assign:
|
||||
{
|
||||
if (m_dialect->flavour != AsmFlavour::Loose)
|
||||
@ -243,13 +270,19 @@ Case Parser::parseCase()
|
||||
|
||||
ForLoop Parser::parseForLoop()
|
||||
{
|
||||
bool outerForLoopBody = m_insideForLoopBody;
|
||||
m_insideForLoopBody = false;
|
||||
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ForLoop forLoop = createWithLocation<ForLoop>();
|
||||
expectToken(Token::For);
|
||||
forLoop.pre = parseBlock();
|
||||
forLoop.condition = make_unique<Expression>(parseExpression());
|
||||
forLoop.post = parseBlock();
|
||||
|
||||
m_insideForLoopBody = true;
|
||||
forLoop.body = parseBlock();
|
||||
m_insideForLoopBody = outerForLoopBody;
|
||||
forLoop.location.end = forLoop.body.location.end;
|
||||
return forLoop;
|
||||
}
|
||||
@ -455,6 +488,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
|
||||
FunctionDefinition Parser::parseFunctionDefinition()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
auto outerForLoopBody = m_insideForLoopBody;
|
||||
m_insideForLoopBody = false;
|
||||
ScopeGuard restoreInsideForLoopBody{[&]() { m_insideForLoopBody = outerForLoopBody; }};
|
||||
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
|
||||
expectToken(Token::Function);
|
||||
funDef.name = expectAsmIdentifier();
|
||||
|
@ -39,7 +39,7 @@ class Parser: public langutil::ParserBase
|
||||
{
|
||||
public:
|
||||
explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
|
||||
ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
|
||||
ParserBase(_errorReporter), m_dialect(std::move(_dialect)), m_insideForLoopBody{false} {}
|
||||
|
||||
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
||||
/// @param _reuseScanner if true, do check for end of input after the `}`.
|
||||
@ -87,6 +87,7 @@ protected:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dialect> m_dialect;
|
||||
bool m_insideForLoopBody;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -224,6 +224,16 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const
|
||||
return out;
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Break const&) const
|
||||
{
|
||||
return "break";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Continue const&) const
|
||||
{
|
||||
return "continue";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Block const& _block) const
|
||||
{
|
||||
if (_block.statements.empty())
|
||||
|
@ -50,6 +50,8 @@ public:
|
||||
std::string operator()(If const& _if) const;
|
||||
std::string operator()(Switch const& _switch) const;
|
||||
std::string operator()(ForLoop const& _forLoop) const;
|
||||
std::string operator()(Break const& _break) const;
|
||||
std::string operator()(Continue const& _continue) const;
|
||||
std::string operator()(Block const& _block) const;
|
||||
|
||||
private:
|
||||
|
@ -63,6 +63,8 @@ public:
|
||||
bool operator()(If const& _if);
|
||||
bool operator()(Switch const& _switch);
|
||||
bool operator()(ForLoop const& _forLoop);
|
||||
bool operator()(Break const&) { return true; }
|
||||
bool operator()(Continue const&) { return true; }
|
||||
bool operator()(Block const& _block);
|
||||
|
||||
private:
|
||||
|
@ -69,7 +69,6 @@ void VariableReferenceCounter::operator()(ForLoop const& _forLoop)
|
||||
m_scope = originalScope;
|
||||
}
|
||||
|
||||
|
||||
void VariableReferenceCounter::operator()(Block const& _block)
|
||||
{
|
||||
Scope* originalScope = m_scope;
|
||||
@ -92,7 +91,6 @@ void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
CodeTransform::CodeTransform(
|
||||
AbstractAssembly& _assembly,
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
@ -605,11 +603,9 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
||||
|
||||
visitStatements(_forLoop.pre.statements);
|
||||
|
||||
// TODO: When we implement break and continue, the labels and the stack heights at that point
|
||||
// have to be stored in a stack.
|
||||
AbstractAssembly::LabelID loopStart = m_assembly.newLabelId();
|
||||
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
|
||||
AbstractAssembly::LabelID postPart = m_assembly.newLabelId();
|
||||
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
|
||||
|
||||
m_assembly.setSourceLocation(_forLoop.location);
|
||||
m_assembly.appendLabel(loopStart);
|
||||
@ -619,6 +615,8 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
||||
m_assembly.appendInstruction(solidity::Instruction::ISZERO);
|
||||
m_assembly.appendJumpToIf(loopEnd);
|
||||
|
||||
int const stackHeightBody = m_assembly.stackHeight();
|
||||
m_context->forLoopStack.emplace(Context::ForLoopLabels{ {postPart, stackHeightBody}, {loopEnd, stackHeightBody} });
|
||||
(*this)(_forLoop.body);
|
||||
|
||||
m_assembly.setSourceLocation(_forLoop.location);
|
||||
@ -631,7 +629,19 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
||||
m_assembly.appendLabel(loopEnd);
|
||||
|
||||
finalizeBlock(_forLoop.pre, stackStartHeight);
|
||||
m_context->forLoopStack.pop();
|
||||
m_scope = originalScope;
|
||||
checkStackHeight(&_forLoop);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(Break const&)
|
||||
{
|
||||
yulAssert(false, "Code generation for break statement in Yul is not implemented yet.");
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(Continue const&)
|
||||
{
|
||||
yulAssert(false, "Code generation for continue statement in Yul is not implemented yet.");
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(Block const& _block)
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
@ -57,6 +59,20 @@ struct CodeTransformContext
|
||||
std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
|
||||
std::map<Scope::Variable const*, int> variableStackHeights;
|
||||
std::map<Scope::Variable const*, unsigned> variableReferences;
|
||||
|
||||
struct JumpInfo
|
||||
{
|
||||
AbstractAssembly::LabelID label; ///< Jump's LabelID to jump to.
|
||||
int targetStackHeight; ///< Stack height after the jump.
|
||||
};
|
||||
|
||||
struct ForLoopLabels
|
||||
{
|
||||
JumpInfo post; ///< Jump info for jumping to post branch.
|
||||
JumpInfo done; ///< Jump info for jumping to done branch.
|
||||
};
|
||||
|
||||
std::stack<ForLoopLabels> forLoopStack;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -166,6 +182,8 @@ public:
|
||||
void operator()(Switch const& _switch);
|
||||
void operator()(FunctionDefinition const&);
|
||||
void operator()(ForLoop const&);
|
||||
void operator()(Break const&);
|
||||
void operator()(Continue const&);
|
||||
void operator()(Block const& _block);
|
||||
|
||||
private:
|
||||
|
@ -138,6 +138,15 @@ Statement ASTCopier::operator()(ForLoop const& _forLoop)
|
||||
translate(_forLoop.body)
|
||||
};
|
||||
}
|
||||
Statement ASTCopier::operator()(Break const& _break)
|
||||
{
|
||||
return Break{ _break };
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Continue const& _continue)
|
||||
{
|
||||
return Continue{ _continue };
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator ()(Block const& _block)
|
||||
{
|
||||
|
@ -58,6 +58,8 @@ public:
|
||||
virtual Statement operator()(Switch const& _switch) = 0;
|
||||
virtual Statement operator()(FunctionDefinition const&) = 0;
|
||||
virtual Statement operator()(ForLoop const&) = 0;
|
||||
virtual Statement operator()(Break const&) = 0;
|
||||
virtual Statement operator()(Continue const&) = 0;
|
||||
virtual Statement operator()(Block const& _block) = 0;
|
||||
};
|
||||
|
||||
@ -83,6 +85,8 @@ public:
|
||||
Statement operator()(Switch const& _switch) override;
|
||||
Statement operator()(FunctionDefinition const&) override;
|
||||
Statement operator()(ForLoop const&) override;
|
||||
Statement operator()(Break const&) override;
|
||||
Statement operator()(Continue const&) override;
|
||||
Statement operator()(Block const& _block) override;
|
||||
|
||||
virtual Expression translate(Expression const& _expression);
|
||||
|
@ -161,6 +161,14 @@ void ASTModifier::operator()(ForLoop& _for)
|
||||
(*this)(_for.body);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Break&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Continue&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Block& _block)
|
||||
{
|
||||
walkVector(_block.statements);
|
||||
|
@ -56,6 +56,8 @@ public:
|
||||
virtual void operator()(Switch const& _switch);
|
||||
virtual void operator()(FunctionDefinition const&);
|
||||
virtual void operator()(ForLoop const&);
|
||||
virtual void operator()(Break const&) {}
|
||||
virtual void operator()(Continue const&) {}
|
||||
virtual void operator()(Block const& _block);
|
||||
|
||||
virtual void visit(Statement const& _st);
|
||||
@ -91,6 +93,8 @@ public:
|
||||
virtual void operator()(Switch& _switch);
|
||||
virtual void operator()(FunctionDefinition&);
|
||||
virtual void operator()(ForLoop&);
|
||||
virtual void operator()(Break&);
|
||||
virtual void operator()(Continue&);
|
||||
virtual void operator()(Block& _block);
|
||||
|
||||
virtual void visit(Statement& _st);
|
||||
|
@ -130,6 +130,16 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
||||
popScope();
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Break&)
|
||||
{
|
||||
yulAssert(false, "Not implemented yet.");
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Continue&)
|
||||
{
|
||||
yulAssert(false, "Not implemented yet.");
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Block& _block)
|
||||
{
|
||||
size_t numScopes = m_variableScopes.size();
|
||||
|
@ -53,6 +53,8 @@ public:
|
||||
void operator()(Switch& _switch) override;
|
||||
void operator()(FunctionDefinition&) override;
|
||||
void operator()(ForLoop&) override;
|
||||
void operator()(Break& _continue) override;
|
||||
void operator()(Continue& _continue) override;
|
||||
void operator()(Block& _block) override;
|
||||
|
||||
protected:
|
||||
|
@ -149,6 +149,16 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
|
||||
join(zeroRuns);
|
||||
}
|
||||
|
||||
void RedundantAssignEliminator::operator()(Break const&)
|
||||
{
|
||||
yulAssert(false, "Not implemented yet.");
|
||||
}
|
||||
|
||||
void RedundantAssignEliminator::operator()(Continue const&)
|
||||
{
|
||||
yulAssert(false, "Not implemented yet.");
|
||||
}
|
||||
|
||||
void RedundantAssignEliminator::operator()(Block const& _block)
|
||||
{
|
||||
// This will set all variables that are declared in this
|
||||
|
@ -112,6 +112,8 @@ public:
|
||||
void operator()(Switch const& _switch) override;
|
||||
void operator()(FunctionDefinition const&) override;
|
||||
void operator()(ForLoop const&) override;
|
||||
void operator()(Break const&) override;
|
||||
void operator()(Continue const&) override;
|
||||
void operator()(Block const& _block) override;
|
||||
|
||||
static void run(Dialect const& _dialect, Block& _ast);
|
||||
|
@ -55,6 +55,8 @@ public:
|
||||
bool statementEqual(Switch const& _lhs, Switch const& _rhs);
|
||||
bool switchCaseEqual(Case const& _lhs, Case const& _rhs);
|
||||
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
||||
bool statementEqual(Break const&, Break const&) { return true; }
|
||||
bool statementEqual(Continue const&, Continue const&) { return true; }
|
||||
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||
private:
|
||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||
|
@ -295,6 +295,74 @@ BOOST_AUTO_TEST_CASE(if_statement)
|
||||
BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {} }", dialect));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_break)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {break} }", dialect));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_break_init)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
CHECK_ERROR_DIALECT(
|
||||
"{ for {let i := 0 break} iszero(eq(i, 10)) {i := add(i, 1)} {} }",
|
||||
SyntaxError,
|
||||
"Keyword break outside for-loop body is not allowed.",
|
||||
dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_break_post)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
CHECK_ERROR_DIALECT(
|
||||
"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) break} {} }",
|
||||
SyntaxError,
|
||||
"Keyword break outside for-loop body is not allowed.",
|
||||
dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_nested_break)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
CHECK_ERROR_DIALECT(
|
||||
"{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { break } } }",
|
||||
SyntaxError,
|
||||
"Keyword break outside for-loop body is not allowed.",
|
||||
dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_continue)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {continue} }", dialect));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_continue_fail_init)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
CHECK_ERROR_DIALECT(
|
||||
"{ for {let i := 0 continue} iszero(eq(i, 10)) {i := add(i, 1)} {} }",
|
||||
SyntaxError,
|
||||
"Keyword continue outside for-loop body is not allowed.",
|
||||
dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(for_statement_continue_fail_post)
|
||||
{
|
||||
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||
CHECK_ERROR_DIALECT(
|
||||
"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) continue} {} }",
|
||||
SyntaxError,
|
||||
"Keyword continue outside for-loop body is not allowed.",
|
||||
dialect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(if_statement_invalid)
|
||||
{
|
||||
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
|
||||
|
@ -105,12 +105,27 @@ void Interpreter::operator()(ForLoop const& _forLoop)
|
||||
visit(statement);
|
||||
while (evaluate(*_forLoop.condition) != 0)
|
||||
{
|
||||
m_state.loopState = LoopState::Default;
|
||||
(*this)(_forLoop.body);
|
||||
if (m_state.loopState == LoopState::Break)
|
||||
break;
|
||||
|
||||
(*this)(_forLoop.post);
|
||||
}
|
||||
m_state.loopState = LoopState::Default;
|
||||
closeScope();
|
||||
}
|
||||
|
||||
void Interpreter::operator()(Break const&)
|
||||
{
|
||||
m_state.loopState = LoopState::Break;
|
||||
}
|
||||
|
||||
void Interpreter::operator()(Continue const&)
|
||||
{
|
||||
m_state.loopState = LoopState::Continue;
|
||||
}
|
||||
|
||||
void Interpreter::operator()(Block const& _block)
|
||||
{
|
||||
openScope();
|
||||
@ -122,7 +137,14 @@ void Interpreter::operator()(Block const& _block)
|
||||
m_functions[funDef.name] = &funDef;
|
||||
m_scopes.back().insert(funDef.name);
|
||||
}
|
||||
ASTWalker::operator()(_block);
|
||||
|
||||
for (auto const& statement: _block.statements)
|
||||
{
|
||||
visit(statement);
|
||||
if (m_state.loopState != LoopState::Default)
|
||||
break;
|
||||
}
|
||||
|
||||
closeScope();
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,13 @@ class InterpreterTerminated: dev::Exception
|
||||
{
|
||||
};
|
||||
|
||||
enum class LoopState
|
||||
{
|
||||
Default,
|
||||
Continue,
|
||||
Break,
|
||||
};
|
||||
|
||||
struct InterpreterState
|
||||
{
|
||||
dev::bytes calldata;
|
||||
@ -65,6 +72,7 @@ struct InterpreterState
|
||||
std::vector<std::string> trace;
|
||||
/// This is actually an input parameter that more or less limits the runtime.
|
||||
size_t maxTraceSize = 0;
|
||||
LoopState loopState = LoopState::Default;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -90,6 +98,8 @@ public:
|
||||
void operator()(Switch const& _switch) override;
|
||||
void operator()(FunctionDefinition const&) override;
|
||||
void operator()(ForLoop const&) override;
|
||||
void operator()(Break const&) override;
|
||||
void operator()(Continue const&) override;
|
||||
void operator()(Block const& _block) override;
|
||||
|
||||
std::vector<std::string> const& trace() const { return m_state.trace; }
|
||||
|
Loading…
Reference in New Issue
Block a user