diff options
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | docs/solidity-by-example.rst | 12 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 42 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.h | 2 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysisInfo.h | 3 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmData.h | 7 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 47 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.h | 1 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.cpp | 14 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.h | 2 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmScopeFiller.cpp | 9 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmScopeFiller.h | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 4 | ||||
-rw-r--r-- | test/libjulia/Parser.cpp | 14 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 52 |
16 files changed, 195 insertions, 26 deletions
@@ -2,7 +2,7 @@ [](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Useful links -To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](http://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](http://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. +To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything. @@ -11,9 +11,9 @@ The changelog for this project can be found [here](https://github.com/ethereum/s Solidity is still under development. So please do not hesitate and open an [issue in GitHub](https://github.com/ethereum/solidity/issues) if you encounter anything strange. ## Building -See the [Solidity documentation](http://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) for build instructions. +See the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) for build instructions. ## How to Contribute -Please see our contribution guidelines in [the Solidity documentation](http://solidity.readthedocs.io/en/latest/contributing.html). +Please see our contribution guidelines in [the Solidity documentation](https://solidity.readthedocs.io/en/latest/contributing.html). Any contributions are welcome! diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 0f60a0e7..993e2c18 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -566,9 +566,9 @@ Safe Remote Purchase _; } - event aborted(); - event purchaseConfirmed(); - event itemReceived(); + event Aborted(); + event PurchaseConfirmed(); + event ItemReceived(); /// Abort the purchase and reclaim the ether. /// Can only be called by the seller before @@ -577,7 +577,7 @@ Safe Remote Purchase onlySeller inState(State.Created) { - aborted(); + Aborted(); state = State.Inactive; seller.transfer(this.balance); } @@ -591,7 +591,7 @@ Safe Remote Purchase condition(msg.value == (2 * value)) payable { - purchaseConfirmed(); + PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; } @@ -602,7 +602,7 @@ Safe Remote Purchase onlyBuyer inState(State.Locked) { - itemReceived(); + ItemReceived(); // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 65b935f2..d022ecf6 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -285,6 +285,48 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) return success; } +bool AsmAnalyzer::operator()(Switch const& _switch) +{ + bool success = true; + + int const initialStackHeight = m_stackHeight; + if (!boost::apply_visitor(*this, *_switch.expression)) + success = false; + expectDeposit(1, initialStackHeight, locationOf(*_switch.expression)); + + set<tuple<LiteralKind, string>> cases; + for (auto const& _case: _switch.cases) + { + if (_case.value) + { + int const initialStackHeight = m_stackHeight; + if (!(*this)(*_case.value)) + success = false; + expectDeposit(1, initialStackHeight, _case.value->location); + m_stackHeight--; + + /// Note: the parser ensures there is only one default case + auto val = make_tuple(_case.value->kind, _case.value->value); + if (!cases.insert(val).second) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Duplicate case defined", + _case.location + )); + success = false; + } + } + + if (!(*this)(_case.body)) + success = false; + } + + m_stackHeight--; + + return success; +} + bool AsmAnalyzer::operator()(Block const& _block) { bool success = true; diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index f09e4b59..9f022b12 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -47,6 +47,7 @@ struct Identifier; struct StackAssignment; struct FunctionDefinition; struct FunctionCall; +struct Switch; struct Scope; @@ -78,6 +79,7 @@ public: bool operator()(assembly::VariableDeclaration const& _variableDeclaration); bool operator()(assembly::FunctionDefinition const& _functionDefinition); bool operator()(assembly::FunctionCall const& _functionCall); + bool operator()(assembly::Switch const& _switch); bool operator()(assembly::Block const& _block); private: diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libsolidity/inlineasm/AsmAnalysisInfo.h index d2253c78..18382db0 100644 --- a/libsolidity/inlineasm/AsmAnalysisInfo.h +++ b/libsolidity/inlineasm/AsmAnalysisInfo.h @@ -43,10 +43,11 @@ struct Identifier; struct StackAssignment; struct FunctionDefinition; struct FunctionCall; +struct Switch; struct Scope; -using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>; +using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>; struct AsmAnalysisInfo { diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 53eafc96..5c66b125 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -266,6 +266,10 @@ public: CodeTransform(m_state, m_assembly, _block, m_identifierAccess, m_initialStackHeight); checkStackHeight(&_block); } + void operator()(assembly::Switch const&) + { + solAssert(false, "Switch not removed during desugaring phase."); + } void operator()(assembly::FunctionDefinition const&) { solAssert(false, "Function definition not removed during desugaring phase."); diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 3b4048c3..72afeef1 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -50,9 +50,10 @@ struct VariableDeclaration; struct FunctionalInstruction; struct FunctionDefinition; struct FunctionCall; +struct Switch; struct Block; -using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>; +using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>; /// Direct EVM instruction (except PUSHi and JUMPDEST) struct Instruction { SourceLocation location; solidity::Instruction instruction; }; @@ -77,6 +78,10 @@ struct VariableDeclaration { SourceLocation location; TypedNameList variables; s struct Block { SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; }; +/// Switch case or default case +struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; }; +/// Switch statement +struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; }; struct LocationExtractor: boost::static_visitor<SourceLocation> { diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 530cd726..605d27be 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -67,6 +67,26 @@ assembly::Statement Parser::parseStatement() return parseFunctionDefinition(); case Token::LBrace: return parseBlock(); + case Token::Switch: + { + assembly::Switch _switch = createWithLocation<assembly::Switch>(); + m_scanner->next(); + _switch.expression = make_shared<Statement>(parseExpression()); + if (_switch.expression->type() == typeid(assembly::Instruction)) + fatalParserError("Instructions are not supported as expressions for switch."); + while (m_scanner->currentToken() == Token::Case) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + fatalParserError("Only one default case allowed."); + else if (m_scanner->currentToken() == Token::Case) + fatalParserError("Case not allowed after default case."); + if (_switch.cases.size() == 0) + fatalParserError("Switch statement without any cases."); + _switch.location.end = _switch.cases.back().body.location.end; + return _switch; + } case Token::Assign: { if (m_julia) @@ -82,9 +102,6 @@ assembly::Statement Parser::parseStatement() expectToken(Token::Identifier); return assignment; } - case Token::Return: // opcode - case Token::Byte: // opcode - case Token::Address: // opcode default: break; } @@ -134,6 +151,26 @@ assembly::Statement Parser::parseStatement() return statement; } +assembly::Case Parser::parseCase() +{ + assembly::Case _case = createWithLocation<assembly::Case>(); + if (m_scanner->currentToken() == Token::Default) + m_scanner->next(); + else if (m_scanner->currentToken() == Token::Case) + { + m_scanner->next(); + assembly::Statement statement = parseElementaryOperation(); + if (statement.type() != typeid(assembly::Literal)) + fatalParserError("Literal expected."); + _case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement))); + } + else + fatalParserError("Case or default case expected."); + _case.body = parseBlock(); + _case.location.end = _case.body.location.end; + return _case; +} + assembly::Statement Parser::parseExpression() { Statement operation = parseElementaryOperation(true); @@ -247,7 +284,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) fatalParserError( m_julia ? "Literal or identifier expected." : - "Expected elementary inline assembly operation." + "Literal, identifier or instruction expected." ); } return ret; @@ -259,7 +296,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() expectToken(Token::Let); while (true) { - varDecl.variables.push_back(parseTypedName()); + varDecl.variables.emplace_back(parseTypedName()); if (m_scanner->currentToken() == Token::Comma) expectToken(Token::Comma); else diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 812762a6..138af337 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -62,6 +62,7 @@ protected: Block parseBlock(); Statement parseStatement(); + Case parseCase(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); std::map<std::string, dev::solidity::Instruction> const& instructions(); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 92b12423..e282e5e8 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -167,6 +167,20 @@ string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall) ")"; } +string AsmPrinter::operator()(Switch const& _switch) +{ + string out = "switch " + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (!_case.value) + out += "\ndefault "; + else + out += "\ncase " + (*this)(*_case.value) + " "; + out += (*this)(_case.body); + } + return out; +} + string AsmPrinter::operator()(Block const& _block) { if (_block.statements.empty()) diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 423eeefa..b0d7fc09 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -40,6 +40,7 @@ struct Assignment; struct VariableDeclaration; struct FunctionDefinition; struct FunctionCall; +struct Switch; struct Block; class AsmPrinter: public boost::static_visitor<std::string> @@ -57,6 +58,7 @@ public: std::string operator()(assembly::VariableDeclaration const& _variableDeclaration); std::string operator()(assembly::FunctionDefinition const& _functionDefinition); std::string operator()(assembly::FunctionCall const& _functionCall); + std::string operator()(assembly::Switch const& _switch); std::string operator()(assembly::Block const& _block); private: diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 05b1b211..7eb6a9ed 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -97,6 +97,15 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) return success; } +bool ScopeFiller::operator()(Switch const& _switch) +{ + bool success = true; + for (auto const& _case: _switch.cases) + if (!(*this)(_case.body)) + success = false; + return success; +} + bool ScopeFiller::operator()(Block const& _block) { bool success = true; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index b1b0833b..c7179b3b 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -46,6 +46,7 @@ struct Identifier; struct StackAssignment; struct FunctionDefinition; struct FunctionCall; +struct Switch; struct Scope; @@ -69,6 +70,7 @@ public: bool operator()(assembly::VariableDeclaration const& _variableDeclaration); bool operator()(assembly::FunctionDefinition const& _functionDefinition); bool operator()(assembly::FunctionCall const&) { return true; } + bool operator()(assembly::Switch const& _switch); bool operator()(assembly::Block const& _block); private: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 72712298..328df91f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -507,8 +507,8 @@ string const& CompilerStack::onChainMetadata(string const& _contractName) const Scanner const& CompilerStack::scanner(string const& _sourceName) const { - if (m_stackState < ParsingSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + if (m_stackState < SourcesSet) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); return *source(_sourceName).scanner; } diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 3931ceb8..31dbd278 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -52,7 +52,10 @@ bool parse(string const& _source, ErrorList& errors) auto scanner = make_shared<Scanner>(CharStream(_source)); auto parserResult = assembly::Parser(errors, true).parse(scanner); if (parserResult) - return true; + { + assembly::AsmAnalysisInfo analysisInfo; + return (assembly::AsmAnalyzer(analysisInfo, errors)).analyze(*parserResult); + } } catch (FatalError const&) { @@ -131,14 +134,9 @@ BOOST_AUTO_TEST_CASE(assignment) BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); } -BOOST_AUTO_TEST_CASE(function_call) -{ - BOOST_CHECK(successParse("{ fun() fun(fun()) }")); -} - BOOST_AUTO_TEST_CASE(vardecl_complex) { - BOOST_CHECK(successParse("{ let y:u256 := 2:u256 let x:u256 := add(7:u256, mul(6:u256, y)) }")); + BOOST_CHECK(successParse("{ function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) }")); } BOOST_AUTO_TEST_CASE(blocks) @@ -158,7 +156,7 @@ BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) BOOST_AUTO_TEST_CASE(function_calls) { - BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(mul(2:u256, 3:u256))) x() } }")); + BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } }")); } BOOST_AUTO_TEST_CASE(tuple_assignment) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 61892761..39cec731 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -223,6 +223,53 @@ BOOST_AUTO_TEST_CASE(variable_use_before_decl) CHECK_PARSE_ERROR("{ let x := mul(2, x) }", DeclarationError, "Variable x used before it was declared."); } +BOOST_AUTO_TEST_CASE(switch_statement) +{ + BOOST_CHECK(successParse("{ switch 42 default {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} default {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} default {} }")); + BOOST_CHECK(successParse("{ switch mul(1, 2) case 1 {} case 2 {} default {} }")); + BOOST_CHECK(successParse("{ function f() -> x {} switch f() case 1 {} case 2 {} default {} }")); +} + +BOOST_AUTO_TEST_CASE(switch_no_cases) +{ + CHECK_PARSE_ERROR("{ switch 42 }", ParserError, "Switch statement without any cases."); +} + +BOOST_AUTO_TEST_CASE(switch_duplicate_case) +{ + CHECK_PARSE_ERROR("{ switch 42 case 1 {} case 1 {} default {} }", DeclarationError, "Duplicate case defined"); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_expression) +{ + CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Expected elementary inline assembly operation."); + CHECK_PARSE_ERROR("{ 1 2 switch mul default {} }", ParserError, "Instructions are not supported as expressions for switch."); +} + +BOOST_AUTO_TEST_CASE(switch_default_before_case) +{ + CHECK_PARSE_ERROR("{ switch 42 default {} case 1 {} }", ParserError, "Case not allowed after default case."); +} + +BOOST_AUTO_TEST_CASE(switch_duplicate_default_case) +{ + CHECK_PARSE_ERROR("{ switch 42 default {} default {} }", ParserError, "Only one default case allowed."); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_case) +{ + CHECK_PARSE_ERROR("{ switch 42 case mul(1, 2) {} case 2 {} default {} }", ParserError, "Literal expected."); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_body) +{ + CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected token LBrace got 'Identifier'"); +} + BOOST_AUTO_TEST_CASE(blocks) { BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); @@ -336,6 +383,11 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) parsePrintCompare(parsed); } +BOOST_AUTO_TEST_CASE(print_switch) +{ + parsePrintCompare("{\n switch 42\n case 1 {\n }\n case 2 {\n }\n default {\n }\n}"); +} + BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) { parsePrintCompare("{\n function f(a, d)\n {\n mstore(a, d)\n }\n function g(a, d) -> x, y\n {\n }\n}"); |