mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #6136 from ethereum/yul-break-continue
[Yul] introduce break/continue keywords.
This commit is contained in:
		
						commit
						cfefa2c1d1
					
				| @ -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