aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--docs/solidity-by-example.rst12
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp42
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h2
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.h3
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp4
-rw-r--r--libsolidity/inlineasm/AsmData.h7
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp47
-rw-r--r--libsolidity/inlineasm/AsmParser.h1
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp14
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h2
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp9
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h2
-rw-r--r--libsolidity/interface/CompilerStack.cpp4
-rw-r--r--test/libjulia/Parser.cpp14
-rw-r--r--test/libsolidity/InlineAssembly.cpp52
16 files changed, 195 insertions, 26 deletions
diff --git a/README.md b/README.md
index bda66996..1fed49fb 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](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}");