From 14fd647b852146cd885426d04bffffbd1b7c08a0 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 9 Nov 2017 00:02:39 -0300 Subject: [PATCH 001/186] Fix event parsing. Refs #3175 --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 14 +++++--------- test/libsolidity/SolidityEndToEndTest.cpp | 6 +++--- test/libsolidity/SolidityParser.cpp | 10 ++++++++++ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index 45521f3e0..85a9fc7f9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Type Checker: More detailed errors for invalid array lengths (such as division by zero). Bugfixes: + * Parser: Disallow event declarations with no parameter list. ### 0.4.18 (2017-10-18) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 821e81d22..05b877b5e 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -644,15 +644,11 @@ ASTPointer Parser::parseEventDefinition() expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); - ASTPointer parameters; - if (m_scanner->currentToken() == Token::LParen) - { - VarDeclParserOptions options; - options.allowIndexed = true; - parameters = parseParameterList(options); - } - else - parameters = createEmptyParameterList(); + + VarDeclParserOptions options; + options.allowIndexed = true; + ASTPointer parameters = parseParameterList(options); + bool anonymous = false; if (m_scanner->currentToken() == Token::Anonymous) { diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 9a8371130..7082b702c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2953,7 +2953,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); function deposit() { Deposit(); } @@ -2995,7 +2995,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); event Deposit(address _addr); event Deposit(address _addr, uint _amount); function deposit() returns (uint) { @@ -3041,7 +3041,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) { char const* sourceCode = R"( contract A { - event Deposit; + event Deposit(); } contract B { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 72473c3e6..861e64083 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -960,6 +960,16 @@ BOOST_AUTO_TEST_CASE(event_arguments_indexed) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(event_with_no_argument_list_fails) +{ + char const* text = R"( + contract c { + event e; + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_CASE(visibility_specifiers) { char const* text = R"( From 565130994ac59689f9d1b42c6bfab2b30cd07128 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Nov 2017 12:45:37 +0100 Subject: [PATCH 002/186] Introduce InlineAsm structs into Julia namespace. --- libjulia/AsmDataForward.h | 52 ++++++++++++++++++++++++ libjulia/backends/evm/EVMCodeTransform.h | 43 ++++++++++---------- 2 files changed, 74 insertions(+), 21 deletions(-) create mode 100644 libjulia/AsmDataForward.h diff --git a/libjulia/AsmDataForward.h b/libjulia/AsmDataForward.h new file mode 100644 index 000000000..3806e321d --- /dev/null +++ b/libjulia/AsmDataForward.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Pull in some identifiers from the solidity::assembly namespace. + */ + +#pragma once + +#include + +namespace dev +{ +namespace julia +{ + +using Instruction = solidity::assembly::Instruction; +using Literal = solidity::assembly::Literal; +using Label = solidity::assembly::Label; +using StackAssignment = solidity::assembly::StackAssignment; +using Identifier = solidity::assembly::Identifier; +using Assignment = solidity::assembly::Assignment; +using VariableDeclaration = solidity::assembly::VariableDeclaration; +using FunctionalInstruction = solidity::assembly::FunctionalInstruction; +using FunctionDefinition = solidity::assembly::FunctionDefinition; +using FunctionCall = solidity::assembly::FunctionCall; +using If = solidity::assembly::If; +using Case = solidity::assembly::Case; +using Switch = solidity::assembly::Switch; +using ForLoop = solidity::assembly::ForLoop; +using Block = solidity::assembly::Block; + +using TypedName = solidity::assembly::TypedName; + +using Statement = boost::variant; + +} +} diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 387720a20..e4cb20ed9 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -20,8 +20,9 @@ #include +#include + #include -#include #include #include @@ -95,38 +96,38 @@ protected: {} public: - void operator()(solidity::assembly::Instruction const& _instruction); - void operator()(solidity::assembly::Literal const& _literal); - void operator()(solidity::assembly::Identifier const& _identifier); - void operator()(solidity::assembly::FunctionalInstruction const& _instr); - void operator()(solidity::assembly::FunctionCall const&); - void operator()(solidity::assembly::Label const& _label); - void operator()(solidity::assembly::StackAssignment const& _assignment); - void operator()(solidity::assembly::Assignment const& _assignment); - void operator()(solidity::assembly::VariableDeclaration const& _varDecl); - void operator()(solidity::assembly::If const& _if); - void operator()(solidity::assembly::Switch const& _switch); - void operator()(solidity::assembly::FunctionDefinition const&); - void operator()(solidity::assembly::ForLoop const&); - void operator()(solidity::assembly::Block const& _block); + void operator()(Instruction const& _instruction); + void operator()(Literal const& _literal); + void operator()(Identifier const& _identifier); + void operator()(FunctionalInstruction const& _instr); + void operator()(FunctionCall const&); + void operator()(Label const& _label); + void operator()(StackAssignment const& _assignment); + void operator()(Assignment const& _assignment); + void operator()(VariableDeclaration const& _varDecl); + void operator()(If const& _if); + void operator()(Switch const& _switch); + void operator()(FunctionDefinition const&); + void operator()(ForLoop const&); + void operator()(Block const& _block); private: - AbstractAssembly::LabelID labelFromIdentifier(solidity::assembly::Identifier const& _identifier); + AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. - void visitExpression(solidity::assembly::Statement const& _expression); + void visitExpression(Statement const& _expression); - void visitStatements(std::vector const& _statements); + void visitStatements(std::vector const& _statements); /// Pops all variables declared in the block and checks that the stack height is equal /// to @a _blackStartStackHeight. - void finalizeBlock(solidity::assembly::Block const& _block, int _blockStartStackHeight); + void finalizeBlock(Block const& _block, int _blockStartStackHeight); - void generateMultiAssignment(std::vector const& _variableNames); - void generateAssignment(solidity::assembly::Identifier const& _variableName); + void generateMultiAssignment(std::vector const& _variableNames); + void generateAssignment(Identifier const& _variableName); /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns From 9f756e37975504936b8f536474d4ea3274d5baeb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 29 Nov 2017 21:43:28 +0000 Subject: [PATCH 003/186] Include missing forward declarations in AsmDataForward --- libsolidity/inlineasm/AsmDataForward.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index d627b41a3..1ab62cc08 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -43,9 +43,12 @@ struct FunctionDefinition; struct FunctionCall; struct If; struct Switch; +struct Case; struct ForLoop; struct Block; +struct TypedName; + using Statement = boost::variant; } From 19e067465a058ca9f40238cb3a928a1113531c9d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 15:23:25 +0200 Subject: [PATCH 004/186] Unary operators and division. --- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/formal/SMTChecker.cpp | 193 +++++++++++++++++++-------- libsolidity/formal/SMTChecker.h | 14 +- libsolidity/formal/SolverInterface.h | 4 + libsolidity/formal/Z3Interface.cpp | 5 +- test/libsolidity/SMTChecker.cpp | 85 ++++++++++++ 6 files changed, 241 insertions(+), 62 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 73047e769..96160a751 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1060,7 +1060,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) _statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + - " (absolute value too large or divison by zero)." + " (absolute value too large or division by zero)." ); else solAssert(false, ""); diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 1050621e2..9e2cf9081 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -55,7 +55,7 @@ void SMTChecker::analyze(SourceUnit const& _source) void SMTChecker::endVisit(VariableDeclaration const& _varDecl) { if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value()) - assignment(_varDecl, *_varDecl.value()); + assignment(_varDecl, *_varDecl.value(), _varDecl.location()); } bool SMTChecker::visit(FunctionDefinition const& _function) @@ -178,8 +178,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) else if (knownVariable(*_varDecl.declarations()[0])) { if (_varDecl.initialValue()) - // TODO more checks? - assignment(*_varDecl.declarations()[0], *_varDecl.initialValue()); + assignment(*_varDecl.declarations()[0], *_varDecl.initialValue(), _varDecl.location()); } else m_errorReporter.warning( @@ -208,7 +207,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) { Declaration const* decl = identifier->annotation().referencedDeclaration; if (knownVariable(*decl)) - assignment(*decl, _assignment.rightHandSide()); + assignment(*decl, _assignment.rightHandSide(), _assignment.location()); else m_errorReporter.warning( _assignment.location(), @@ -230,7 +229,81 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) "Assertion checker does not yet implement tules and inline arrays." ); else - m_interface->addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); + defineExpr(_tuple, expr(*_tuple.components()[0])); +} + +void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) +{ + checkCondition( + _value < minValue(_type), + _location, + "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", + "value", + &_value + ); + checkCondition( + _value > maxValue(_type), + _location, + "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", + "value", + &_value + ); +} + +void SMTChecker::endVisit(UnaryOperation const& _op) +{ + switch (_op.getOperator()) + { + case Token::Not: // ! + { + solAssert(_op.annotation().type->category() == Type::Category::Bool, ""); + defineExpr(_op, !expr(_op.subExpression())); + break; + } + case Token::Inc: // ++ (pre- or postfix) + case Token::Dec: // -- (pre- or postfix) + { + solAssert(_op.annotation().type->category() == Type::Category::Integer, ""); + solAssert(_op.subExpression().annotation().lValueRequested, ""); + if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) + { + Declaration const* decl = identifier->annotation().referencedDeclaration; + if (knownVariable(*decl)) + { + auto innerValue = currentValue(*decl); + auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; + assignment(*decl, newValue, _op.location()); + defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement such assignments." + ); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement such increments / decrements." + ); + break; + } + case Token::Add: // + + defineExpr(_op, expr(_op.subExpression())); + break; + case Token::Sub: // - + { + defineExpr(_op, 0 - expr(_op.subExpression())); + if (auto intType = dynamic_cast(_op.annotation().type.get())) + checkUnderOverflow(expr(_op), *intType, _op.location()); + break; + } + default: + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); + } } void SMTChecker::endVisit(BinaryOperation const& _op) @@ -274,10 +347,8 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); m_interface->addAssertion(expr(*args[0])); - checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); - // TODO is there something meaningful we can check here? - // We can check whether the condition is always fulfilled or never fulfilled. } } @@ -290,7 +361,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) // Will be translated as part of the node that requested the lvalue. } else if (dynamic_cast(_identifier.annotation().type.get())) - m_interface->addAssertion(expr(_identifier) == currentValue(*decl)); + defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) @@ -306,10 +377,10 @@ void SMTChecker::endVisit(Literal const& _literal) if (RationalNumberType const* rational = dynamic_cast(&type)) solAssert(!rational->isFractional(), ""); - m_interface->addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); + defineExpr(_literal, smt::Expression(type.literalValue(&_literal))); } else if (type.category() == Type::Category::Bool) - m_interface->addAssertion(expr(_literal) == smt::Expression(_literal.token() == Token::TrueLiteral ? true : false)); + defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false)); else m_errorReporter.warning( _literal.location(), @@ -326,6 +397,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) case Token::Add: case Token::Sub: case Token::Mul: + case Token::Div: { solAssert(_op.annotation().commonType, ""); solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); @@ -335,27 +407,19 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : + op == Token::Div ? left / right : /*op == Token::Mul*/ left * right ); - // Overflow check - auto const& intType = dynamic_cast(*_op.annotation().commonType); - checkCondition( - value < minValue(intType), - _op.location(), - "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")", - "value", - &value - ); - checkCondition( - value > maxValue(intType), - _op.location(), - "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")", - "value", - &value - ); + if (_op.getOperator() == Token::Div) + { + checkCondition(right == 0, _op.location(), "Division by zero", "value", &right); + m_interface->addAssertion(right != 0); + } - m_interface->addAssertion(expr(_op) == value); + checkUnderOverflow(value, dynamic_cast(*_op.annotation().commonType), _op.location()); + + defineExpr(_op, value); break; } default: @@ -383,7 +447,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) /*op == Token::GreaterThanOrEqual*/ (left >= right) ); // TODO: check that other values for op are not possible. - m_interface->addAssertion(expr(_op) == value); + defineExpr(_op, value); } else m_errorReporter.warning( @@ -400,9 +464,9 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) { // @TODO check that both of them are not constant if (_op.getOperator() == Token::And) - m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression())); else - m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression())); } else m_errorReporter.warning( @@ -411,11 +475,17 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) ); } -void SMTChecker::assignment(Declaration const& _variable, Expression const& _value) +void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location) { - // TODO more checks? - // TODO add restrictions about type (might be assignment from smaller type) - m_interface->addAssertion(newValue(_variable) == expr(_value)); + assignment(_variable, expr(_value), _location); +} + +void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location) +{ + TypePointer type = _variable.type(); + if (auto const* intType = dynamic_cast(type.get())) + checkUnderOverflow(_value, *intType, _location); + m_interface->addAssertion(newValue(_variable) == _value); } void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) @@ -694,31 +764,40 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_expressions.count(&_e)) - { - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) - { - case Type::Category::RationalNumber: - { - if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); - break; - default: - solAssert(false, "Type not implemented."); - } - } + solAssert(m_expressions.count(&_e), ""); return m_expressions.at(&_e); } +void SMTChecker::createExpr(Expression const& _e) +{ + solAssert(!m_expressions.count(&_e), ""); + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) + { + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + } + case Type::Category::Integer: + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + case Type::Category::Bool: + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + break; + default: + solAssert(false, "Type not implemented."); + } +} + +void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) +{ + createExpr(_e); + m_interface->addAssertion(expr(_e) == _value); +} + smt::Expression SMTChecker::var(Declaration const& _decl) { solAssert(m_variables.count(&_decl), ""); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 8e07d74da..cc3aca813 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -57,6 +57,7 @@ private: virtual void endVisit(ExpressionStatement const& _node) override; virtual void endVisit(Assignment const& _node) override; virtual void endVisit(TupleExpression const& _node) override; + virtual void endVisit(UnaryOperation const& _node) override; virtual void endVisit(BinaryOperation const& _node) override; virtual void endVisit(FunctionCall const& _node) override; virtual void endVisit(Identifier const& _node) override; @@ -66,7 +67,8 @@ private: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); - void assignment(Declaration const& _variable, Expression const& _value); + void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); + void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); // Visits the branch given by the statement, pushes and pops the SMT checker. // @param _condition if present, asserts that this condition is true within the branch. @@ -88,6 +90,9 @@ private: Expression const& _condition, std::string const& _description ); + /// Checks that the value is in the range given by the type. + void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location); + std::pair> checkSatisifableAndGenerateModel(std::vector const& _expressionsToEvaluate); @@ -126,9 +131,12 @@ private: using VariableSequenceCounters = std::map; - /// Returns the expression corresponding to the AST node. Creates a new expression - /// if it does not exist yet. + /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); + /// Creates the expression (value can be arbitrary) + void createExpr(Expression const& _e); + /// Creates the expression and sets its value. + void defineExpr(Expression const& _e, smt::Expression _value); /// Returns the function declaration corresponding to the given variable. /// The function takes one argument which is the "sequence number". smt::Expression var(Declaration const& _decl); diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index c9adf863e..74c993e85 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -120,6 +120,10 @@ public: { return Expression("*", std::move(_a), std::move(_b), Sort::Int); } + friend Expression operator/(Expression _a, Expression _b) + { + return Expression("/", std::move(_a), std::move(_b), Sort::Int); + } Expression operator()(Expression _a) const { solAssert( diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index e5c1aef44..769e6edb1 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -127,7 +127,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) {">=", 2}, {"+", 2}, {"-", 2}, - {"*", 2} + {"*", 2}, + {"/", 2} }; string const& n = _expr.name; if (m_functions.count(n)) @@ -173,6 +174,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] - arguments[1]; else if (n == "*") return arguments[0] * arguments[1]; + else if (n == "/") + return arguments[0] / arguments[1]; // Cannot reach here. solAssert(false, ""); return arguments[0]; diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 667d666b4..7aa555e9a 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -458,6 +458,91 @@ BOOST_AUTO_TEST_CASE(for_loop) CHECK_WARNING(text, "Assertion violation"); } +BOOST_AUTO_TEST_CASE(division) +{ + string text = R"( + contract C { + function f(uint x, uint y) public pure returns (uint) { + return x / y; + } + } + )"; + CHECK_WARNING(text, "Division by zero"); + text = R"( + contract C { + function f(uint x, uint y) public pure returns (uint) { + require(y != 0); + return x / y; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure returns (int) { + require(y != 0); + return x / y; + } + } + )"; + CHECK_WARNING(text, "Overflow"); + text = R"( + contract C { + function f(int x, int y) public pure returns (int) { + require(y != 0); + require(y != -1); + return x / y; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(division_truncates_correctly) +{ + string text = R"( + contract C { + function f(uint x, uint y) public pure { + x = 7; + y = 2; + assert(x / y == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = 7; + y = 2; + assert(x / y == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = -7; + y = 2; + assert(x / y == -3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = -7; + y = -2; + int r = x / y; + assert(r == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } From d160ec8595171e16dd404e9b859aa37aed2fe0a4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:20:46 +0200 Subject: [PATCH 005/186] Fix signed division. --- libsolidity/formal/SMTChecker.cpp | 18 ++++++++++++++++-- libsolidity/formal/SMTChecker.h | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 9e2cf9081..c20c63c6f 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -401,13 +401,14 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); + auto const& intType = dynamic_cast(*_op.annotation().commonType); smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : - op == Token::Div ? left / right : + op == Token::Div ? division(left, right, intType) : /*op == Token::Mul*/ left * right ); @@ -417,7 +418,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) m_interface->addAssertion(right != 0); } - checkUnderOverflow(value, dynamic_cast(*_op.annotation().commonType), _op.location()); + checkUnderOverflow(value, intType, _op.location()); defineExpr(_op, value); break; @@ -475,6 +476,19 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) ); } +smt::Expression SMTChecker::division(smt::Expression _left, smt::Expression _right, IntegerType const& _type) +{ + // Signed division in SMTLIB2 rounds differently for negative division. + if (_type.isSigned()) + return (smt::Expression::ite( + _left >= 0, + smt::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), + smt::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) + )); + else + return _left / _right; +} + void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location) { assignment(_variable, expr(_value), _location); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index cc3aca813..e7481ccaf 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -67,6 +67,10 @@ private: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); + /// Division expression in the given type. Requires special treatment because + /// of rounding for signed division. + smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); + void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); From 00e252a39faf99802ab46f32b41950c6f825063a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:31:28 +0200 Subject: [PATCH 006/186] Another test for division. --- test/libsolidity/SMTChecker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 7aa555e9a..eac788352 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -530,6 +530,16 @@ BOOST_AUTO_TEST_CASE(division_truncates_correctly) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = 7; + y = -2; + assert(x / y == -3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { function f(int x, int y) public pure { From a2569833203a952f265acc3511ace52cd9cec4c0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:39:29 +0200 Subject: [PATCH 007/186] Fix expression creation problems. --- libsolidity/formal/SMTChecker.cpp | 49 +++++++++++++++++++------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index c20c63c6f..a22e35d64 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -207,7 +207,10 @@ void SMTChecker::endVisit(Assignment const& _assignment) { Declaration const* decl = identifier->annotation().referencedDeclaration; if (knownVariable(*decl)) + { assignment(*decl, _assignment.rightHandSide(), _assignment.location()); + defineExpr(_assignment, expr(_assignment.rightHandSide())); + } else m_errorReporter.warning( _assignment.location(), @@ -778,31 +781,39 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - solAssert(m_expressions.count(&_e), ""); + if (!m_expressions.count(&_e)) + { + m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." ); + createExpr(_e); + } return m_expressions.at(&_e); } void SMTChecker::createExpr(Expression const& _e) { - solAssert(!m_expressions.count(&_e), ""); - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) + if (m_expressions.count(&_e)) + m_errorReporter.warning(_e.location(), "Internal error: Expression created twice in SMT solver." ); + else { - case Type::Category::RationalNumber: - { - if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); - break; - default: - solAssert(false, "Type not implemented."); + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) + { + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + } + case Type::Category::Integer: + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + case Type::Category::Bool: + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + break; + default: + solAssert(false, "Type not implemented."); + } } } From 0971c00b21e8ad7f61faa700a8413641e85b182e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Nov 2017 17:50:13 +0100 Subject: [PATCH 008/186] Increment version to 0.4.20. --- CMakeLists.txt | 2 +- Changelog.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 24bea3b3e..105de8515 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.19") +set(PROJECT_VERSION "0.4.20") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index 9a30986a6..fd256af8a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +### 0.4.20 (unreleased) + +Features: + +Bugfixes: + ### 0.4.19 (2017-11-30) Features: From cbb3370836df17dc1edc1497a2caa6fb930cd4dd Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Nov 2017 19:12:39 +0100 Subject: [PATCH 009/186] Fix namespace ambiguity. --- libjulia/backends/evm/EVMAssembly.cpp | 2 +- libjulia/backends/evm/EVMCodeTransform.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libjulia/backends/evm/EVMAssembly.cpp index 1d499b200..07ad05c9c 100644 --- a/libjulia/backends/evm/EVMAssembly.cpp +++ b/libjulia/backends/evm/EVMAssembly.cpp @@ -26,7 +26,7 @@ using namespace std; using namespace dev; -using namespace julia; +using namespace dev::julia; namespace { diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 13d9d0117..099b83fe0 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -31,7 +31,8 @@ using namespace std; using namespace dev; using namespace dev::julia; using namespace dev::solidity; -using namespace dev::solidity::assembly; + +using Scope = dev::solidity::assembly::Scope; void CodeTransform::operator()(VariableDeclaration const& _varDecl) { From f8ef68be1869b2b5a5872839c64d4667aef41c4b Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Dec 2017 15:18:39 +0100 Subject: [PATCH 010/186] Manual deploy to docker. --- scripts/docker_deploy_manual.sh | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 scripts/docker_deploy_manual.sh diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh new file mode 100755 index 000000000..c098f4eec --- /dev/null +++ b/scripts/docker_deploy_manual.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env sh + +set -e + +if [ -z "$1" ] +then + echo "Usage: $0 " + exit 1 +fi +branch="$1" + +#docker login + +DIR=$(mktemp -d) +( +cd "$DIR" + +git clone --depth 2 https://github.com/ethereum/solidity.git -b "$branch" +cd solidity +commithash=$(git rev-parse --short=8 HEAD) +echo -n "$commithash" > commit_hash.txt +version=$($(dirname "$0")/get_version.sh) +if [ "$branch" = "release" -o "$branch" = v"$version" ] +then + echo -n > prerelease.txt +else + date -u +"nightly.%Y.%-m.%-d" > prerelease.txt +fi + +rm -rf .git +docker build -t ethereum/solc:build -f scripts/Dockerfile . +tmp_container=$(docker create ethereum/solc:build sh) +if [ "$branch" = "develop" ] +then + docker tag ethereum/solc:build ethereum/solc:nightly; + docker tag ethereum/solc:build ethereum/solc:nightly-"$version"-"$commithash" + docker push ethereum/solc:nightly-"$version"-"$commithash"; + docker push ethereum/solc:nightly; +elif [ "$branch" = v"$version" ] +then + docker tag ethereum/solc:build ethereum/solc:stable; + docker tag ethereum/solc:build ethereum/solc:"$version"; + docker push ethereum/solc:stable; + docker push ethereum/solc:"$version"; +else + echo "Not publishing docker image from branch or tag $branch" +fi +) +rm -rf "$DIR" From 43bb915454e210ed7c201eb58b33c7ffe8dfbdb1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Dec 2017 14:10:49 +0100 Subject: [PATCH 011/186] Rename arguments to paramaters and returns to returnVariables. --- libjulia/backends/evm/EVMCodeTransform.cpp | 16 ++++++++-------- libsolidity/inlineasm/AsmAnalysis.cpp | 4 ++-- libsolidity/inlineasm/AsmData.h | 2 +- libsolidity/inlineasm/AsmParser.cpp | 4 ++-- libsolidity/inlineasm/AsmPrinter.cpp | 6 +++--- libsolidity/inlineasm/AsmScopeFiller.cpp | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 13d9d0117..efc991665 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -289,7 +289,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) solAssert(m_info.scopes.at(&_function.body), ""); Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); solAssert(varScope, ""); - for (auto const& v: _function.arguments | boost::adaptors::reversed) + for (auto const& v: _function.parameters | boost::adaptors::reversed) { auto& var = boost::get(varScope->identifiers.at(v.name)); m_context->variableStackHeights[&var] = height++; @@ -302,7 +302,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) if (m_evm15) { m_assembly.appendJumpTo(afterFunction, -stackHeightBefore); - m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size()); + m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size()); } else { @@ -311,7 +311,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) } m_stackAdjustment += localStackAdjustment; - for (auto const& v: _function.returns) + for (auto const& v: _function.returnVariables) { auto& var = boost::get(varScope->identifiers.at(v.name)); m_context->variableStackHeights[&var] = height++; @@ -341,9 +341,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function) // modified parallel to the actual stack. vector stackLayout; if (!m_evm15) - stackLayout.push_back(_function.returns.size()); // Move return label to the top - stackLayout += vector(_function.arguments.size(), -1); // discard all arguments - for (size_t i = 0; i < _function.returns.size(); ++i) + stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top + stackLayout += vector(_function.parameters.size(), -1); // discard all arguments + for (size_t i = 0; i < _function.returnVariables.size(); ++i) stackLayout.push_back(i); // Move return values down, but keep order. solAssert(stackLayout.size() <= 17, "Stack too deep"); @@ -363,9 +363,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function) } if (m_evm15) - m_assembly.appendReturnsub(_function.returns.size(), stackHeightBefore); + m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore); else - m_assembly.appendJump(stackHeightBefore - _function.returns.size()); + m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size()); m_stackAdjustment -= localStackAdjustment; m_assembly.appendLabel(afterFunction); checkStackHeight(&_function); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2804ddfcb..c55355e22 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -217,14 +217,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); solAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) { expectValidType(var.type, var.location); m_activeVariables.insert(&boost::get(varScope.identifiers.at(var.name))); } int const stackHeight = m_stackHeight; - m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); + m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size(); bool success = (*this)(_funDef.body); diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index a792a1b8d..e1753af4a 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -67,7 +67,7 @@ struct VariableDeclaration { SourceLocation location; TypedNameList variables; s /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; }; +struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. struct If { SourceLocation location; std::shared_ptr condition; Block body; }; /// Switch case or default case diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 8f171005f..1555804ba 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -419,7 +419,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::LParen); while (currentToken() != Token::RParen) { - funDef.arguments.emplace_back(parseTypedName()); + funDef.parameters.emplace_back(parseTypedName()); if (currentToken() == Token::RParen) break; expectToken(Token::Comma); @@ -431,7 +431,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::GreaterThan); while (true) { - funDef.returns.emplace_back(parseTypedName()); + funDef.returnVariables.emplace_back(parseTypedName()); if (currentToken() == Token::LBrace) break; expectToken(Token::Comma); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 0f1832440..804fb1d54 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -144,17 +144,17 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin { string out = "function " + _functionDefinition.name + "("; out += boost::algorithm::join( - _functionDefinition.arguments | boost::adaptors::transformed( + _functionDefinition.parameters | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " ); out += ")"; - if (!_functionDefinition.returns.empty()) + if (!_functionDefinition.returnVariables.empty()) { out += " -> "; out += boost::algorithm::join( - _functionDefinition.returns | boost::adaptors::transformed( + _functionDefinition.returnVariables | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 77ae9102c..0984e7d25 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -71,10 +71,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; vector arguments; - for (auto const& _argument: _funDef.arguments) + for (auto const& _argument: _funDef.parameters) arguments.push_back(_argument.type); vector returns; - for (auto const& _return: _funDef.returns) + for (auto const& _return: _funDef.returnVariables) returns.push_back(_return.type); if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { @@ -91,7 +91,7 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) varScope.superScope = m_currentScope; m_currentScope = &varScope; varScope.functionScope = true; - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) if (!registerVariable(var, _funDef.location, varScope)) success = false; From 134cc73e8e6948553ba22691fc7e7661433dd123 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 29 Oct 2017 13:52:57 +0000 Subject: [PATCH 012/186] Enable struct abi example with experimental pragma --- docs/abi-spec.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index c93ce25bd..e968fb063 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -377,10 +377,14 @@ As an example, the code :: + pragma solidity ^0.4.19; + pragma experimental ABIEncoderV2; + contract Test { struct S { uint a; uint[] b; T[] c; } struct T { uint x; uint y; } function f(S s, T t, uint a) { } + function g() returns (S s, T t, uint a) {} } would result in the JSON: From 29b30a95e9cc8fc79ec40c1abdca55128e28a23f Mon Sep 17 00:00:00 2001 From: Ezra Epstein Date: Fri, 1 Dec 2017 14:00:44 -0500 Subject: [PATCH 013/186] link to wiki for additional info on napspec --- docs/metadata.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/metadata.rst b/docs/metadata.rst index dbde87e86..5e37219e1 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -131,6 +131,8 @@ user interface for the contract. Furthermore, Mist can use the userdoc to display a confirmation message to the user whenever they interact with the contract. +Additional information about Ethereum Natural Specification (NatSpec) can be found `here `_. + Usage for Source Code Verification ================================== From 07101c13385dba6c103d9d0f51588638d94d5b81 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Dec 2017 22:58:05 +0100 Subject: [PATCH 014/186] Rename to ASTDataForward.h --- libjulia/{AsmDataForward.h => ASTDataForward.h} | 0 libjulia/backends/evm/EVMCodeTransform.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename libjulia/{AsmDataForward.h => ASTDataForward.h} (100%) diff --git a/libjulia/AsmDataForward.h b/libjulia/ASTDataForward.h similarity index 100% rename from libjulia/AsmDataForward.h rename to libjulia/ASTDataForward.h diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index e4cb20ed9..577cc8bab 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -20,7 +20,7 @@ #include -#include +#include #include From 6d609557b6b2f13d7d8f8b13c2ceb1472266860a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:40:28 +0200 Subject: [PATCH 015/186] Fix test. --- test/libsolidity/SMTChecker.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index eac788352..38d026010 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -351,9 +351,9 @@ BOOST_AUTO_TEST_CASE(while_loop_simple) // Check that side-effects of condition are taken into account text = R"( contract C { - function f(uint x) public pure { + function f(uint x, uint y) public pure { x = 7; - while ((x = 5) > 0) { + while ((x = y) > 0) { } assert(x == 7); } @@ -545,8 +545,7 @@ BOOST_AUTO_TEST_CASE(division_truncates_correctly) function f(int x, int y) public pure { x = -7; y = -2; - int r = x / y; - assert(r == 3); + assert(x / y == 3); } } )"; From ca16099ad046c6e67debf2796f655e83328341bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20S=C3=A1?= Date: Tue, 5 Dec 2017 03:20:35 +0000 Subject: [PATCH 016/186] Removed manual adjustment of stack of the docs Removed the section fo the docs regarding manual adjustment of the stack given the `unbalanced stack` check prevents its use --- docs/assembly.rst | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index c233985ba..a4fa88c63 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -447,31 +447,6 @@ will have a wrong impression about the stack height at label ``two``: three: } -This problem can be fixed by manually adjusting the stack height for the -assembler - you can provide a stack height delta that is added -to the stack height just prior to the label. -Note that you will not have to care about these things if you just use -loops and assembly-level functions. - -As an example how this can be done in extreme cases, please see the following. - -.. code:: - - { - let x := 8 - jump(two) - 0 // This code is unreachable but will adjust the stack height correctly - one: - x := 9 // Now x can be accessed properly. - jump(three) - pop // Similar negative correction. - two: - 7 // push something onto the stack - jump(one) - three: - pop // We have to pop the manually pushed value here again. - } - Declaring Assembly-Local Variables ---------------------------------- From 861210f543cea615d7779fb7b1bc133349ae971a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Nov 2017 12:45:37 +0100 Subject: [PATCH 017/186] Disambiguator. --- libjulia/optimiser/ASTCopier.cpp | 179 +++++++++++++++++++++++++++ libjulia/optimiser/ASTCopier.h | 101 +++++++++++++++ libjulia/optimiser/Disambiguator.cpp | 85 +++++++++++++ libjulia/optimiser/Disambiguator.h | 70 +++++++++++ test/libjulia/Disambiguator.cpp | 177 ++++++++++++++++++++++++++ 5 files changed, 612 insertions(+) create mode 100644 libjulia/optimiser/ASTCopier.cpp create mode 100644 libjulia/optimiser/ASTCopier.h create mode 100644 libjulia/optimiser/Disambiguator.cpp create mode 100644 libjulia/optimiser/Disambiguator.h create mode 100644 test/libjulia/Disambiguator.cpp diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp new file mode 100644 index 000000000..2f9eade31 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.cpp @@ -0,0 +1,179 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; + + +shared_ptr ASTCopier::run() +{ + return make_shared(translate(m_block)); +} + +Statement ASTCopier::operator()(Instruction const& _instruction) +{ + return _instruction; +} + +Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) +{ + return VariableDeclaration{ + _varDecl.location, + translateVector(_varDecl.variables), + translate(_varDecl.value) + }; +} + +Statement ASTCopier::operator()(Assignment const& _assignment) +{ + return Assignment{ + _assignment.location, + translateVector(_assignment.variableNames), + translate(_assignment.value) + }; +} + +Statement ASTCopier::operator()(StackAssignment const&) +{ + solAssert(false, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(Label const&) +{ + solAssert(false, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(FunctionCall const& _call) +{ + return FunctionCall{ + _call.location, + translate(_call.functionName), + translateVector(_call.arguments) + }; +} + +Statement ASTCopier::operator()(FunctionalInstruction const& _instruction) +{ + return FunctionalInstruction{ + _instruction.location, + _instruction.instruction, + translateVector(_instruction.arguments) + }; +} + +Statement ASTCopier::operator()(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Statement ASTCopier::operator()(Literal const& _literal) +{ + return translate(_literal); +} + +Statement ASTCopier::operator()(If const& _if) +{ + return If{_if.location, translate(_if.condition), translate(_if.body)}; +} + +Statement ASTCopier::operator()(Switch const& _switch) +{ + return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)}; +} + +Statement ASTCopier::operator()(FunctionDefinition const& _function) +{ + string translatedName = translateIdentifier(_function.name); + + enterFunction(_function); + ScopeGuard g([&]() { this->leaveFunction(_function); }); + + return FunctionDefinition{ + _function.location, + move(translatedName), + translateVector(_function.parameters), + translateVector(_function.returnVariables), + translate(_function.body) + }; +} + +Statement ASTCopier::operator()(ForLoop const& _forLoop) +{ + enterScope(_forLoop.pre); + ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); }); + + return ForLoop{ + _forLoop.location, + translate(_forLoop.pre), + translate(_forLoop.condition), + translate(_forLoop.post), + translate(_forLoop.body) + }; +} + +Statement ASTCopier::operator ()(Block const& _block) +{ + return translate(_block); +} + +Statement ASTCopier::translate(Statement const& _statement) +{ + return boost::apply_visitor(*this, _statement); +} + +Block ASTCopier::translate(Block const& _block) +{ + enterScope(_block); + ScopeGuard g([&]() { this->leaveScope(_block); }); + + return Block{_block.location, translateVector(_block.statements)}; +} + +Case ASTCopier::translate(Case const& _case) +{ + return Case{_case.location, translate(_case.value), translate(_case.body)}; +} + +Identifier ASTCopier::translate(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Literal ASTCopier::translate(Literal const& _literal) +{ + return _literal; +} + +TypedName ASTCopier::translate(TypedName const& _typedName) +{ + return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type}; +} + diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h new file mode 100644 index 000000000..759dce317 --- /dev/null +++ b/libjulia/optimiser/ASTCopier.h @@ -0,0 +1,101 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#pragma once + +#include + +#include +#include + +#include +#include + +namespace dev +{ +namespace julia +{ + +/** + * Creates a copy of a iulia AST potentially replacing identifier names. + * Base class to be extended. + */ +class ASTCopier: public boost::static_visitor +{ +public: + ASTCopier(Block const& _block): + m_block(_block) + {} + + std::shared_ptr run(); + +public: + Statement operator()(Literal const& _literal); + Statement operator()(Instruction const& _instruction); + Statement operator()(Identifier const& _identifier); + Statement operator()(FunctionalInstruction const& _instr); + Statement operator()(FunctionCall const&); + Statement operator()(Label const& _label); + Statement operator()(StackAssignment const& _assignment); + Statement operator()(Assignment const& _assignment); + Statement operator()(VariableDeclaration const& _varDecl); + Statement operator()(If const& _if); + Statement operator()(Switch const& _switch); + Statement operator()(FunctionDefinition const&); + Statement operator()(ForLoop const&); + Statement operator()(Block const& _block); + +protected: + template + std::vector translateVector(std::vector const& _values); + + template + std::shared_ptr translate(std::shared_ptr const& _v) + { + return _v ? std::make_shared(translate(*_v)) : nullptr; + } + Statement translate(Statement const& _statement); + Block translate(Block const& _block); + Case translate(Case const& _case); + Identifier translate(Identifier const& _identifier); + Literal translate(Literal const& _literal); + TypedName translate(TypedName const& _typedName); + + virtual void enterScope(Block const& _block) = 0; + virtual void leaveScope(Block const& _block) = 0; + virtual void enterFunction(FunctionDefinition const& _function) = 0; + virtual void leaveFunction(FunctionDefinition const& _function) = 0; + virtual std::string translateIdentifier(std::string const& _name) { return _name; } + + Block const& m_block; +}; + +template +std::vector ASTCopier::translateVector(std::vector const& _values) +{ + std::vector translated; + for (auto const& v: _values) + translated.emplace_back(translate(v)); + return translated; +} + + +} +} diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp new file mode 100644 index 000000000..df2984e50 --- /dev/null +++ b/libjulia/optimiser/Disambiguator.cpp @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#include + +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + +using Scope = dev::solidity::assembly::Scope; + +string Disambiguator::translateIdentifier(string const& _originalName) +{ + solAssert(!m_scopes.empty() && m_scopes.back(), ""); + Scope::Identifier const* id = m_scopes.back()->lookup(_originalName); + solAssert(id, ""); + if (!m_translations.count(id)) + { + string translated = _originalName; + size_t suffix = 0; + while (m_usedNames.count(translated)) + { + suffix++; + translated = _originalName + "_" + std::to_string(suffix); + } + m_usedNames.insert(translated); + m_translations[id] = translated; + } + return m_translations.at(id); +} + +void Disambiguator::enterScope(Block const& _block) +{ + enterScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::leaveScope(Block const& _block) +{ + leaveScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::enterFunction(FunctionDefinition const& _function) +{ + enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::leaveFunction(FunctionDefinition const& _function) +{ + leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::enterScopeInternal(Scope& _scope) +{ + m_scopes.push_back(&_scope); +} + +void Disambiguator::leaveScopeInternal(Scope& _scope) +{ + solAssert(!m_scopes.empty(), ""); + solAssert(m_scopes.back() == &_scope, ""); + m_scopes.pop_back(); +} diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h new file mode 100644 index 000000000..39615cf76 --- /dev/null +++ b/libjulia/optimiser/Disambiguator.h @@ -0,0 +1,70 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#pragma once + +#include + +#include + +#include + +#include +#include + +#include + +namespace dev +{ +namespace julia +{ +class EVMAssembly; + +/** + * Creates a copy of a iulia AST replacing all identifiers by unique names. + */ +class Disambiguator: public ASTCopier +{ +public: + Disambiguator( + Block const& _block, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo + ): ASTCopier(_block), m_info(_analysisInfo) + {} + +protected: + virtual void enterScope(Block const& _block) override; + virtual void leaveScope(Block const& _block) override; + virtual void enterFunction(FunctionDefinition const& _function) override; + virtual void leaveFunction(FunctionDefinition const& _function) override; + virtual std::string translateIdentifier(std::string const& _name) override; + + void enterScopeInternal(solidity::assembly::Scope& _scope); + void leaveScopeInternal(solidity::assembly::Scope& _scope); + + solidity::assembly::AsmAnalysisInfo const& m_info; + + std::vector m_scopes; + std::map m_translations; + std::set m_usedNames; +}; + +} +} diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp new file mode 100644 index 000000000..baee46415 --- /dev/null +++ b/test/libjulia/Disambiguator.cpp @@ -0,0 +1,177 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Unit tests for the iulia name disambiguator. + */ + +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace dev::solidity::assembly; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +void printErrors(ErrorList const& _errors, Scanner const& _scanner) +{ + for (auto const& error: _errors) + SourceReferenceFormatter::printExceptionInformation( + cout, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const&) -> Scanner const& { return _scanner; } + ); +} + + +pair, shared_ptr> parse(string const& _source) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared(); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, true); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +string disambiguate(string const& _source) +{ + auto result = parse(_source); + julia::Disambiguator disambiguator(*result.first, *result.second); + return assembly::AsmPrinter(true)(*disambiguator.run()); +} + +string format(string const& _source) +{ + return assembly::AsmPrinter(true)(*parse(_source).first); +} + +} + +#define CHECK(_original, _expectation)\ +do\ +{\ + BOOST_CHECK_EQUAL(disambiguate(_original), format(_expectation));\ + BOOST_CHECK_EQUAL(disambiguate(_original), disambiguate(disambiguate(_original)));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaDisambiguator) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(variables) +{ + CHECK( + "{ { let a:u256 } { let a:u256 } }", + "{ { let a:u256 } { let a_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_clash) +{ + CHECK( + "{ { let a:u256 let a_1:u256 } { let a:u256 } }", + "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_inside_functions) +{ + CHECK( + "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }", + "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(function_call) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }", + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }" + ); +} + +BOOST_AUTO_TEST_CASE(for_statement) +{ + CHECK( + "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }", + "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(switch_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(if_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() + + +} +} +} // end namespaces From cba58629d24404ed7553d16bb2ad064f95f9fe37 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 23 Nov 2017 15:35:14 +0100 Subject: [PATCH 018/186] Refactor iulia tests. --- test/libjulia/Common.cpp | 87 +++++++++++++++++++++++++++++++++ test/libjulia/Common.h | 54 ++++++++++++++++++++ test/libjulia/Disambiguator.cpp | 86 +++----------------------------- 3 files changed, 148 insertions(+), 79 deletions(-) create mode 100644 test/libjulia/Common.cpp create mode 100644 test/libjulia/Common.h diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp new file mode 100644 index 000000000..30bb22ea7 --- /dev/null +++ b/test/libjulia/Common.cpp @@ -0,0 +1,87 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Common functions the iulia tests. + */ + +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace std; +using namespace dev::julia; +using namespace dev::solidity; + +void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) +{ + for (auto const& error: _errors) + SourceReferenceFormatter::printExceptionInformation( + cout, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const&) -> Scanner const& { return _scanner; } + ); +} + + +pair, shared_ptr> dev::julia::test::parse(string const& _source, bool _julia) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared(); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, true); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +shared_ptr dev::julia::test::disambiguate(string const& _source) +{ + auto result = parse(_source); + Disambiguator disambiguator(*result.first, *result.second); + return disambiguator.run(); +} + +string dev::julia::test::format(string const& _source) +{ + return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first); +} diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h new file mode 100644 index 000000000..55fa03da1 --- /dev/null +++ b/test/libjulia/Common.h @@ -0,0 +1,54 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Common functions the iulia tests. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector>; +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace julia +{ +namespace test +{ + +void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); +std::pair, std::shared_ptr> parse(std::string const& _source); +std::shared_ptr disambiguate(std::string const& _source); +std::string format(std::string const& _source); + +} +} +} diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp index baee46415..f5f883bac 100644 --- a/test/libjulia/Disambiguator.cpp +++ b/test/libjulia/Disambiguator.cpp @@ -19,90 +19,23 @@ * Unit tests for the iulia name disambiguator. */ -#include +#include -#include -#include #include -#include -#include - -#include - #include -#include -#include - using namespace std; -using namespace dev::solidity::assembly; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -namespace -{ - -void printErrors(ErrorList const& _errors, Scanner const& _scanner) -{ - for (auto const& error: _errors) - SourceReferenceFormatter::printExceptionInformation( - cout, - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const&) -> Scanner const& { return _scanner; } - ); -} - - -pair, shared_ptr> parse(string const& _source) -{ - ErrorList errors; - ErrorReporter errorReporter(errors); - auto scanner = make_shared(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); - if (parserResult) - { - BOOST_REQUIRE(errorReporter.errors().empty()); - auto analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, true); - if (analyzer.analyze(*parserResult)) - { - BOOST_REQUIRE(errorReporter.errors().empty()); - return make_pair(parserResult, analysisInfo); - } - } - printErrors(errors, *scanner); - BOOST_FAIL("Invalid source."); - - // Unreachable. - return {}; -} - -string disambiguate(string const& _source) -{ - auto result = parse(_source); - julia::Disambiguator disambiguator(*result.first, *result.second); - return assembly::AsmPrinter(true)(*disambiguator.run()); -} - -string format(string const& _source) -{ - return assembly::AsmPrinter(true)(*parse(_source).first); -} - -} +using namespace dev::julia::test; +using namespace dev::solidity; #define CHECK(_original, _expectation)\ do\ {\ - BOOST_CHECK_EQUAL(disambiguate(_original), format(_expectation));\ - BOOST_CHECK_EQUAL(disambiguate(_original), disambiguate(disambiguate(_original)));\ + assembly::AsmPrinter p(true);\ + string result = p(*disambiguate(_original));\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ + BOOST_CHECK_EQUAL(result, p(*disambiguate(result)));\ }\ while(false) @@ -170,8 +103,3 @@ BOOST_AUTO_TEST_CASE(if_statement) } BOOST_AUTO_TEST_SUITE_END() - - -} -} -} // end namespaces From 30d7afc2e3fd27333e463a9127011ec1f7470d3e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Nov 2017 15:00:23 +0100 Subject: [PATCH 019/186] Simplify disambiguator. --- libjulia/optimiser/ASTCopier.cpp | 5 ----- libjulia/optimiser/ASTCopier.h | 18 +++++------------- libjulia/optimiser/Disambiguator.h | 6 ++---- test/libjulia/Common.cpp | 13 ++++++------- test/libjulia/Common.h | 7 ++++--- test/libjulia/Disambiguator.cpp | 4 ++-- 6 files changed, 19 insertions(+), 34 deletions(-) diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp index 2f9eade31..d777d731b 100644 --- a/libjulia/optimiser/ASTCopier.cpp +++ b/libjulia/optimiser/ASTCopier.cpp @@ -31,11 +31,6 @@ using namespace dev; using namespace dev::julia; -shared_ptr ASTCopier::run() -{ - return make_shared(translate(m_block)); -} - Statement ASTCopier::operator()(Instruction const& _instruction) { return _instruction; diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h index 759dce317..0658fd509 100644 --- a/libjulia/optimiser/ASTCopier.h +++ b/libjulia/optimiser/ASTCopier.h @@ -27,6 +27,7 @@ #include #include +#include namespace dev { @@ -39,13 +40,6 @@ namespace julia */ class ASTCopier: public boost::static_visitor { -public: - ASTCopier(Block const& _block): - m_block(_block) - {} - - std::shared_ptr run(); - public: Statement operator()(Literal const& _literal); Statement operator()(Instruction const& _instruction); @@ -78,13 +72,11 @@ protected: Literal translate(Literal const& _literal); TypedName translate(TypedName const& _typedName); - virtual void enterScope(Block const& _block) = 0; - virtual void leaveScope(Block const& _block) = 0; - virtual void enterFunction(FunctionDefinition const& _function) = 0; - virtual void leaveFunction(FunctionDefinition const& _function) = 0; + virtual void enterScope(Block const&) { } + virtual void leaveScope(Block const&) { } + virtual void enterFunction(FunctionDefinition const&) { } + virtual void leaveFunction(FunctionDefinition const&) { } virtual std::string translateIdentifier(std::string const& _name) { return _name; } - - Block const& m_block; }; template diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h index 39615cf76..cc9488d53 100644 --- a/libjulia/optimiser/Disambiguator.h +++ b/libjulia/optimiser/Disambiguator.h @@ -43,10 +43,8 @@ class EVMAssembly; class Disambiguator: public ASTCopier { public: - Disambiguator( - Block const& _block, - solidity::assembly::AsmAnalysisInfo const& _analysisInfo - ): ASTCopier(_block), m_info(_analysisInfo) + Disambiguator(solidity::assembly::AsmAnalysisInfo const& _analysisInfo): + m_info(_analysisInfo) {} protected: diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 30bb22ea7..da1538f3a 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -55,12 +55,12 @@ pair, shared_ptr> dev::julia::test: ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, _julia).parse(scanner); if (parserResult) { BOOST_REQUIRE(errorReporter.errors().empty()); auto analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, true); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, _julia); if (analyzer.analyze(*parserResult)) { BOOST_REQUIRE(errorReporter.errors().empty()); @@ -74,14 +74,13 @@ pair, shared_ptr> dev::julia::test: return {}; } -shared_ptr dev::julia::test::disambiguate(string const& _source) +assembly::Block dev::julia::test::disambiguate(string const& _source, bool _julia) { - auto result = parse(_source); - Disambiguator disambiguator(*result.first, *result.second); - return disambiguator.run(); + auto result = parse(_source, _julia); + return boost::get(Disambiguator(*result.second)(*result.first)); } -string dev::julia::test::format(string const& _source) +string dev::julia::test::format(string const& _source, bool _julia) { return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first); } diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h index 55fa03da1..1371101cb 100644 --- a/test/libjulia/Common.h +++ b/test/libjulia/Common.h @@ -45,9 +45,10 @@ namespace test { void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); -std::pair, std::shared_ptr> parse(std::string const& _source); -std::shared_ptr disambiguate(std::string const& _source); -std::string format(std::string const& _source); +std::pair, std::shared_ptr> +parse(std::string const& _source, bool _julia = true); +solidity::assembly::Block disambiguate(std::string const& _source, bool _julia = true); +std::string format(std::string const& _source, bool _julia = true); } } diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp index f5f883bac..a63384493 100644 --- a/test/libjulia/Disambiguator.cpp +++ b/test/libjulia/Disambiguator.cpp @@ -33,9 +33,9 @@ using namespace dev::solidity; do\ {\ assembly::AsmPrinter p(true);\ - string result = p(*disambiguate(_original));\ + string result = p(disambiguate(_original));\ BOOST_CHECK_EQUAL(result, format(_expectation));\ - BOOST_CHECK_EQUAL(result, p(*disambiguate(result)));\ + BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\ }\ while(false) From c6df1cdaaa3d743b24e655a1e94eecf4ba49de3b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 30 Nov 2017 01:19:49 +0100 Subject: [PATCH 020/186] Generic AST walker. --- libjulia/optimiser/ASTWalker.cpp | 149 +++++++++++++++++++++++++++ libjulia/optimiser/ASTWalker.h | 100 ++++++++++++++++++ libjulia/optimiser/NameCollector.cpp | 44 ++++++++ libjulia/optimiser/NameCollector.h | 52 ++++++++++ 4 files changed, 345 insertions(+) create mode 100644 libjulia/optimiser/ASTWalker.cpp create mode 100644 libjulia/optimiser/ASTWalker.h create mode 100644 libjulia/optimiser/NameCollector.cpp create mode 100644 libjulia/optimiser/NameCollector.h diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp new file mode 100644 index 000000000..786afbe07 --- /dev/null +++ b/libjulia/optimiser/ASTWalker.cpp @@ -0,0 +1,149 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Generic AST walker. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + + +void ASTWalker::operator()(FunctionalInstruction const& _instr) +{ + walkVector(_instr.arguments | boost::adaptors::reversed); +} + +void ASTWalker::operator()(FunctionCall const& _funCall) +{ + walkVector(_funCall.arguments | boost::adaptors::reversed); +} + +void ASTWalker::operator()(Assignment const& _assignment) +{ + for (auto const& name: _assignment.variableNames) + (*this)(name); + boost::apply_visitor(*this, *_assignment.value); +} + +void ASTWalker::operator()(VariableDeclaration const& _varDecl) +{ + if (_varDecl.value) + boost::apply_visitor(*this, *_varDecl.value); +} + +void ASTWalker::operator()(If const& _if) +{ + boost::apply_visitor(*this, *_if.condition); + (*this)(_if.body); +} + +void ASTWalker::operator()(Switch const& _switch) +{ + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } +} + +void ASTWalker::operator()(FunctionDefinition const& _fun) +{ + (*this)(_fun.body); +} + +void ASTWalker::operator()(ForLoop const& _for) +{ + (*this)(_for.pre); + boost::apply_visitor(*this, *_for.condition); + (*this)(_for.post); + (*this)(_for.body); +} + +void ASTWalker::operator()(Block const& _block) +{ + walkVector(_block.statements); +} + +void ASTModifier::operator()(FunctionalInstruction& _instr) +{ + walkVector(_instr.arguments | boost::adaptors::reversed); +} + +void ASTModifier::operator()(FunctionCall& _funCall) +{ + walkVector(_funCall.arguments | boost::adaptors::reversed); +} + +void ASTModifier::operator()(Assignment& _assignment) +{ + for (auto& name: _assignment.variableNames) + (*this)(name); + boost::apply_visitor(*this, *_assignment.value); +} + +void ASTModifier::operator()(VariableDeclaration& _varDecl) +{ + if (_varDecl.value) + boost::apply_visitor(*this, *_varDecl.value); +} + +void ASTModifier::operator()(If& _if) +{ + boost::apply_visitor(*this, *_if.condition); + (*this)(_if.body); +} + +void ASTModifier::operator()(Switch& _switch) +{ + boost::apply_visitor(*this, *_switch.expression); + for (auto& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } +} + +void ASTModifier::operator()(FunctionDefinition& _fun) +{ + (*this)(_fun.body); +} + +void ASTModifier::operator()(ForLoop& _for) +{ + (*this)(_for.pre); + boost::apply_visitor(*this, *_for.condition); + (*this)(_for.post); + (*this)(_for.body); +} + +void ASTModifier::operator()(Block& _block) +{ + walkVector(_block.statements); +} diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h new file mode 100644 index 000000000..01499a503 --- /dev/null +++ b/libjulia/optimiser/ASTWalker.h @@ -0,0 +1,100 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Generic AST walker. + */ + +#pragma once + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace dev +{ +namespace julia +{ + +/** + * Generic AST walker. + */ +class ASTWalker: public boost::static_visitor<> +{ +public: + virtual void operator()(Literal const&) {} + virtual void operator()(Instruction const&) { solAssert(false, ""); } + virtual void operator()(Identifier const&) {} + virtual void operator()(FunctionalInstruction const& _instr); + virtual void operator()(FunctionCall const& _funCall); + virtual void operator()(Label const&) { solAssert(false, ""); } + virtual void operator()(StackAssignment const&) { solAssert(false, ""); } + virtual void operator()(Assignment const& _assignment); + virtual void operator()(VariableDeclaration const& _varDecl); + virtual void operator()(If const& _if); + virtual void operator()(Switch const& _switch); + virtual void operator()(FunctionDefinition const&); + virtual void operator()(ForLoop const&); + virtual void operator()(Block const& _block); + +protected: + template + void walkVector(T const& _statements) + { + for (auto const& st: _statements) + boost::apply_visitor(*this, st); + } +}; + +/** + * Generic AST modifier (i.e. non-const version of ASTWalker). + */ +class ASTModifier: public boost::static_visitor<> +{ +public: + virtual void operator()(Literal&) {} + virtual void operator()(Instruction&) { solAssert(false, ""); } + virtual void operator()(Identifier&) {} + virtual void operator()(FunctionalInstruction& _instr); + virtual void operator()(FunctionCall& _funCall); + virtual void operator()(Label&) { solAssert(false, ""); } + virtual void operator()(StackAssignment&) { solAssert(false, ""); } + virtual void operator()(Assignment& _assignment); + virtual void operator()(VariableDeclaration& _varDecl); + virtual void operator()(If& _if); + virtual void operator()(Switch& _switch); + virtual void operator()(FunctionDefinition&); + virtual void operator()(ForLoop&); + virtual void operator()(Block& _block); + +protected: + template + void walkVector(T&& _statements) + { + for (auto& st: _statements) + boost::apply_visitor(*this, st); + } +}; + +} +} diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp new file mode 100644 index 000000000..7b4c4793e --- /dev/null +++ b/libjulia/optimiser/NameCollector.cpp @@ -0,0 +1,44 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Specific AST walker that collects all defined names. + */ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; + +void NameCollector::operator()(VariableDeclaration const& _varDecl) +{ + for (auto const& var: _varDecl.variables) + m_names.insert(var.name); +} + +void NameCollector::operator ()(FunctionDefinition const& _funDef) +{ + m_names.insert(_funDef.name); + m_functions[_funDef.name] = &_funDef; + for (auto const arg: _funDef.parameters) + m_names.insert(arg.name); + for (auto const ret: _funDef.returnVariables) + m_names.insert(ret.name); + ASTWalker::operator ()(_funDef); +} diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h new file mode 100644 index 000000000..b7e38f461 --- /dev/null +++ b/libjulia/optimiser/NameCollector.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Specific AST walker that collects all defined names. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace dev +{ +namespace julia +{ + +/** + * Specific AST walker that collects all defined names. + */ +class NameCollector: public ASTWalker +{ +public: + using ASTWalker::operator (); + virtual void operator()(VariableDeclaration const& _varDecl) override; + virtual void operator()(FunctionDefinition const& _funDef) override; + + std::set const& names() const { return m_names; } + std::map const& functions() const { return m_functions; } +private: + std::set m_names; + std::map m_functions; +}; + +} +} From 745eefa36f9bc04c91cb28e81bd16f8d01a11c7c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 13:44:20 +0000 Subject: [PATCH 021/186] Split Instruction and FunctionalInstruction in Julia --- libjulia/backends/evm/EVMCodeTransform.cpp | 9 +++++---- libjulia/optimiser/ASTCopier.cpp | 5 +++-- libsolidity/analysis/ViewPureChecker.cpp | 14 +++++++++----- libsolidity/inlineasm/AsmAnalysis.cpp | 7 ++++--- libsolidity/inlineasm/AsmData.h | 2 +- libsolidity/inlineasm/AsmParser.cpp | 7 ++++--- libsolidity/inlineasm/AsmPrinter.cpp | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 733e39e9b..f92939be9 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -125,11 +125,11 @@ void CodeTransform::operator()(FunctionCall const& _call) void CodeTransform::operator()(FunctionalInstruction const& _instruction) { if (m_evm15 && ( - _instruction.instruction.instruction == solidity::Instruction::JUMP || - _instruction.instruction.instruction == solidity::Instruction::JUMPI + _instruction.instruction == solidity::Instruction::JUMP || + _instruction.instruction == solidity::Instruction::JUMPI )) { - bool const isJumpI = _instruction.instruction.instruction == solidity::Instruction::JUMPI; + bool const isJumpI = _instruction.instruction == solidity::Instruction::JUMPI; if (isJumpI) { solAssert(_instruction.arguments.size() == 2, ""); @@ -150,7 +150,8 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) { for (auto const& arg: _instruction.arguments | boost::adaptors::reversed) visitExpression(arg); - (*this)(_instruction.instruction); + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); } checkStackHeight(&_instruction); } diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp index d777d731b..a461f434b 100644 --- a/libjulia/optimiser/ASTCopier.cpp +++ b/libjulia/optimiser/ASTCopier.cpp @@ -31,9 +31,10 @@ using namespace dev; using namespace dev::julia; -Statement ASTCopier::operator()(Instruction const& _instruction) +Statement ASTCopier::operator()(Instruction const&) { - return _instruction; + solAssert(false, "Invalid operation."); + return {}; } Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 7e41fc16c..6788cb059 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -40,16 +40,13 @@ public: void operator()(assembly::Label const&) { } void operator()(assembly::Instruction const& _instruction) { - if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) - m_reportMutability(StateMutability::NonPayable, _instruction.location); - else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) - m_reportMutability(StateMutability::View, _instruction.location); + checkInstruction(_instruction.location, _instruction.instruction); } void operator()(assembly::Literal const&) {} void operator()(assembly::Identifier const&) {} void operator()(assembly::FunctionalInstruction const& _instr) { - (*this)(_instr.instruction); + checkInstruction(_instr.location, _instr.instruction); for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } @@ -102,6 +99,13 @@ public: private: std::function m_reportMutability; + void checkInstruction(SourceLocation _location, solidity::Instruction _instruction) + { + if (eth::SemanticInformation::invalidInViewFunctions(_instruction)) + m_reportMutability(StateMutability::NonPayable, _location); + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction)) + m_reportMutability(StateMutability::View, _location); + } }; } diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index c55355e22..5c03342d0 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -146,10 +146,11 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) if (!expectExpression(arg)) success = false; // Parser already checks that the number of arguments is correct. - solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), ""); - if (!(*this)(_instr.instruction)) - success = false; + auto const& info = instructionInfo(_instr.instruction); + solAssert(info.args == int(_instr.arguments.size()), ""); + m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instr] = m_stackHeight; + warnOnInstructions(_instr.instruction, _instr.location); return success; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index e1753af4a..11e56fae1 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -60,7 +60,7 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; /// the same amount of items as the number of variables. struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector arguments; }; +struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 1555804ba..4f8802a0b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -448,10 +448,11 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) if (_instruction.type() == typeid(Instruction)) { solAssert(!m_julia, "Instructions are invalid in JULIA"); + Instruction const& instruction = std::move(boost::get(_instruction)); FunctionalInstruction ret; - ret.instruction = std::move(boost::get(_instruction)); - ret.location = ret.instruction.location; - solidity::Instruction instr = ret.instruction.instruction; + ret.instruction = instruction.instruction; + ret.location = std::move(instruction.location); + solidity::Instruction instr = ret.instruction; InstructionInfo instrInfo = instructionInfo(instr); if (solidity::isDupInstruction(instr)) fatalParserError("DUPi instructions not allowed for functional notation"); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 804fb1d54..c72586cbb 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -94,7 +94,7 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional { solAssert(!m_julia, ""); return - (*this)(_functionalInstruction.instruction) + + boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), From 6769a9a503fd35ae147650634d82d1321e6b8826 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 5 Dec 2017 19:46:16 +0100 Subject: [PATCH 022/186] Make the modifier more flexible. --- libjulia/optimiser/ASTWalker.cpp | 10 +++++----- libjulia/optimiser/ASTWalker.h | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp index 786afbe07..0caef04e0 100644 --- a/libjulia/optimiser/ASTWalker.cpp +++ b/libjulia/optimiser/ASTWalker.cpp @@ -104,24 +104,24 @@ void ASTModifier::operator()(Assignment& _assignment) { for (auto& name: _assignment.variableNames) (*this)(name); - boost::apply_visitor(*this, *_assignment.value); + visit(*_assignment.value); } void ASTModifier::operator()(VariableDeclaration& _varDecl) { if (_varDecl.value) - boost::apply_visitor(*this, *_varDecl.value); + visit(*_varDecl.value); } void ASTModifier::operator()(If& _if) { - boost::apply_visitor(*this, *_if.condition); + visit(*_if.condition); (*this)(_if.body); } void ASTModifier::operator()(Switch& _switch) { - boost::apply_visitor(*this, *_switch.expression); + visit(*_switch.expression); for (auto& _case: _switch.cases) { if (_case.value) @@ -138,7 +138,7 @@ void ASTModifier::operator()(FunctionDefinition& _fun) void ASTModifier::operator()(ForLoop& _for) { (*this)(_for.pre); - boost::apply_visitor(*this, *_for.condition); + visit(*_for.condition); (*this)(_for.post); (*this)(_for.body); } diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h index 01499a503..8bd867d5a 100644 --- a/libjulia/optimiser/ASTWalker.h +++ b/libjulia/optimiser/ASTWalker.h @@ -92,7 +92,11 @@ protected: void walkVector(T&& _statements) { for (auto& st: _statements) - boost::apply_visitor(*this, st); + visit(st); + } + virtual void visit(Statement& _st) + { + boost::apply_visitor(*this, _st); } }; From d34054ef582be09992e79a72125800484b25159f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 5 Dec 2017 20:06:34 +0100 Subject: [PATCH 023/186] copier --- libjulia/optimiser/ASTCopier.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h index 0658fd509..5dde2ce9d 100644 --- a/libjulia/optimiser/ASTCopier.h +++ b/libjulia/optimiser/ASTCopier.h @@ -56,6 +56,8 @@ public: Statement operator()(ForLoop const&); Statement operator()(Block const& _block); + virtual Statement translate(Statement const& _statement); + protected: template std::vector translateVector(std::vector const& _values); @@ -65,7 +67,6 @@ protected: { return _v ? std::make_shared(translate(*_v)) : nullptr; } - Statement translate(Statement const& _statement); Block translate(Block const& _block); Case translate(Case const& _case); Identifier translate(Identifier const& _identifier); From 07825e90fa261660625fb3731f6283db83789e8b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 5 Dec 2017 20:07:31 +0100 Subject: [PATCH 024/186] substitution --- libjulia/optimiser/Substitution.cpp | 39 ++++++++++++++++++++++ libjulia/optimiser/Substitution.h | 51 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 libjulia/optimiser/Substitution.cpp create mode 100644 libjulia/optimiser/Substitution.h diff --git a/libjulia/optimiser/Substitution.cpp b/libjulia/optimiser/Substitution.cpp new file mode 100644 index 000000000..a49f1f7a7 --- /dev/null +++ b/libjulia/optimiser/Substitution.cpp @@ -0,0 +1,39 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Specific AST copier that replaces certain identifiers with expressions. + */ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; + +Statement Substitution::translate(Statement const& _statement) +{ + if (_statement.type() == typeid(Identifier)) + { + string const& name = boost::get(_statement).name; + if (m_substitutions.count(name)) + // No recursive substitution + return ASTCopier().translate(*m_substitutions.at(name)); + } + return ASTCopier::translate(_statement); +} diff --git a/libjulia/optimiser/Substitution.h b/libjulia/optimiser/Substitution.h new file mode 100644 index 000000000..10bdf32e6 --- /dev/null +++ b/libjulia/optimiser/Substitution.h @@ -0,0 +1,51 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Specific AST copier that replaces certain identifiers with expressions. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace dev +{ +namespace julia +{ + +/** + * Specific AST copier that replaces certain identifiers with expressions. + * Only works on ASTs that are expressions. + */ +class Substitution: public ASTCopier +{ +public: + Substitution(std::map const& _substitutions): + m_substitutions(_substitutions) + {} + virtual Statement translate(Statement const& _statement) override; + +private: + std::map const& m_substitutions; +}; + +} +} From bc875f6b9c140d351ec8ae96d2c8e29979017d57 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 10:44:28 +0000 Subject: [PATCH 025/186] Warn for assembly labels too --- Changelog.md | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 5 +++-- test/libsolidity/InlineAssembly.cpp | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index fd256af8a..248165bbc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.20 (unreleased) Features: + * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions). Bugfixes: diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 5c03342d0..04b8417f0 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -56,6 +56,7 @@ bool AsmAnalyzer::operator()(Label const& _label) { solAssert(!m_julia, ""); m_info.stackHeightInfo[&_label] = m_stackHeight; + warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; } @@ -523,10 +524,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); - if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI) + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, - "Jump instructions are low-level EVM features that can lead to " + "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " "Please consider using \"switch\" or \"for\" statements instead." ); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index e9fb84316..506721c1d 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -714,6 +714,7 @@ BOOST_AUTO_TEST_CASE(jump_warning) CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions"); CHECK_PARSE_WARNING("{ a: jump(a) }", Warning, "Jump instructions"); CHECK_PARSE_WARNING("{ a: jumpi(a, 2) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions"); } BOOST_AUTO_TEST_SUITE_END() From 793537e08926190728e93a666a5fadc6d0765f63 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 10:45:44 +0000 Subject: [PATCH 026/186] Suggest the "if" statement too instead of jumps --- libsolidity/inlineasm/AsmAnalysis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 04b8417f0..049af65fb 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -529,6 +529,6 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio _location, "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " - "Please consider using \"switch\" or \"for\" statements instead." + "Please consider using \"switch\", \"if\" or \"for\" statements instead." ); } From d57afb20fa32be3d5575b8c5dcd98f39ce4e90d0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 20:27:50 +0000 Subject: [PATCH 027/186] Fix warning test for jumps in assembly --- test/libsolidity/InlineAssembly.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 506721c1d..82bafd49d 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -712,8 +712,8 @@ BOOST_AUTO_TEST_CASE(jump_warning) { CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions"); - CHECK_PARSE_WARNING("{ a: jump(a) }", Warning, "Jump instructions"); - CHECK_PARSE_WARNING("{ a: jumpi(a, 2) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ jump(44) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ jumpi(44, 2) }", Warning, "Jump instructions"); CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions"); } From 2d6fe522a6369cc5e5420f5a29a7f95108ea454e Mon Sep 17 00:00:00 2001 From: Yuriy Kashnikov Date: Wed, 6 Dec 2017 15:58:24 +0700 Subject: [PATCH 028/186] Handle git release tag properly Build process will check current commit and if it is tagged starting with a `v` then it will create `prerelease.txt` file in the top directory. --- scripts/build.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 3785e1c13..362accedf 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,5 +1,9 @@ #!/usr/bin/env bash +if [[ "$(git tag --points-at HEAD 2>/dev/null)"=="v*" ]]; then + touch prerelease.txt +fi + if [ -z "$1" ]; then BUILD_TYPE=Release else @@ -20,4 +24,4 @@ fi if [ -z $CI ]; then echo "Installing solc and soltest" install solc/solc /usr/local/bin && install test/soltest /usr/local/bin -fi \ No newline at end of file +fi From 4634ce825b1f9f810978c15fe12d652a3f833537 Mon Sep 17 00:00:00 2001 From: Yuriy Kashnikov Date: Wed, 6 Dec 2017 21:14:54 +0700 Subject: [PATCH 029/186] Review fixes --- scripts/build.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 362accedf..bddbb97a9 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,9 +1,5 @@ #!/usr/bin/env bash -if [[ "$(git tag --points-at HEAD 2>/dev/null)"=="v*" ]]; then - touch prerelease.txt -fi - if [ -z "$1" ]; then BUILD_TYPE=Release else @@ -11,6 +7,11 @@ else fi cd $(dirname "$0")/.. && + +if [[ "$(git tag --points-at HEAD 2>/dev/null)" == v* ]]; then + touch prerelease.txt +fi + mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" && From ef579617113d10a58cd6fb717f35de7e22c4cfd1 Mon Sep 17 00:00:00 2001 From: Kamuela Franco Date: Sat, 9 Dec 2017 14:53:40 +0000 Subject: [PATCH 030/186] Update style-guide.rst to include enum style --- docs/style-guide.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index a438b3d0a..5b6f42a2e 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -739,6 +739,12 @@ Modifier Names Use mixedCase. Examples: ``onlyBy``, ``onlyAfter``, ``onlyDuringThePreSale``. +Enums +===== + +Enums, in the style of simple type declarations, should be named using the CapWords style. Examples: ``TokenGroup``, ``Frame``, ``HashStyle``, ``CharacterLocation``. + + Avoiding Naming Collisions ========================== From e9d256ddf4322050f9b834abd3400f1cb5377f68 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 2 Dec 2017 01:01:55 +0000 Subject: [PATCH 031/186] Suggest the experimental ABI encoder if using structs as function parameters --- Changelog.md | 2 ++ libsolidity/analysis/TypeChecker.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/Changelog.md b/Changelog.md index 248165bbc..cf6498a16 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,8 @@ Features: * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions). Bugfixes: + * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters + (instead of an internal compiler error). ### 0.4.19 (2017-11-30) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 96160a751..daa5471ad 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -570,6 +570,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + if ( + _function.visibility() > FunctionDefinition::Visibility::Internal && + type(*var)->category() == Type::Category::Struct && + !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) + ) + m_errorReporter.typeError( + var->location(), + "Structs are only supported in the new experimental ABI encoder. " + "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ); var->accept(*this); } From 9e36c189e50b033d25d3ab2342a7a0e673035a68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 Dec 2017 13:00:45 +0100 Subject: [PATCH 032/186] Fix struct encoding warning for libraries. --- libsolidity/analysis/TypeChecker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index daa5471ad..a578ad0f7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -573,6 +573,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if ( _function.visibility() > FunctionDefinition::Visibility::Internal && type(*var)->category() == Type::Category::Struct && + !type(*var)->dataStoredIn(DataLocation::Storage) && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) ) m_errorReporter.typeError( From b32d7a84fc8c69b96cb3f03091327d3e4657e495 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 Dec 2017 02:39:39 +0000 Subject: [PATCH 033/186] Move some struct tests to require ABIEncoderV2 --- test/libsolidity/SMTChecker.cpp | 1 + test/libsolidity/SolidityABIJSON.cpp | 3 +++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 38d026010..3a65aa433 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -94,6 +94,7 @@ BOOST_AUTO_TEST_CASE(warn_on_typecast) BOOST_AUTO_TEST_CASE(warn_on_struct) { string text = R"( + pragma experimental ABIEncoderV2; contract C { struct A { uint a; uint b; } function f() public pure returns (A) { diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 339627300..26bfb6d08 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -942,6 +942,7 @@ BOOST_AUTO_TEST_CASE(function_type) BOOST_AUTO_TEST_CASE(return_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[2] x; } @@ -991,6 +992,7 @@ BOOST_AUTO_TEST_CASE(return_structs) BOOST_AUTO_TEST_CASE(return_structs_with_contracts) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { C[] x; C y; } function f() returns (S s, C c) { @@ -1090,6 +1092,7 @@ BOOST_AUTO_TEST_CASE(event_structs) BOOST_AUTO_TEST_CASE(structs_in_libraries) { char const* text = R"( + pragma experimental ABIEncoderV2; library L { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 97d359e8e..39dba0dea 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -602,6 +602,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) BOOST_AUTO_TEST_CASE(external_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} @@ -629,6 +630,7 @@ BOOST_AUTO_TEST_CASE(external_structs) BOOST_AUTO_TEST_CASE(external_structs_in_libraries) { char const* text = R"( + pragma experimental ABIEncoderV2; library Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} @@ -3511,6 +3513,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_used) BOOST_AUTO_TEST_CASE(library_memory_struct) { char const* text = R"( + pragma experimental ABIEncoderV2; library c { struct S { uint x; } function f() public returns (S ) {} @@ -5696,6 +5699,7 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) BOOST_AUTO_TEST_CASE(return_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[] x; } From 1734d4456189b4a313a07e7f3e24cb01f5057fc3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 Sep 2017 03:50:16 +0100 Subject: [PATCH 034/186] Rename jsonCompiler to libsolc --- CMakeLists.txt | 3 ++- libsolc/CMakeLists.txt | 9 +++++++++ solc/jsonCompiler.cpp => libsolc/libsolc.cpp | 2 +- solc/jsonCompiler.h => libsolc/libsolc.h | 0 scripts/bytecodecompare/storebytecode.sh | 2 +- scripts/test_emscripten.sh | 2 +- scripts/travis-emscripten/build_emscripten.sh | 4 ++-- solc/CMakeLists.txt | 8 -------- test/CMakeLists.txt | 4 ++-- test/fuzzer.cpp | 2 +- test/libsolidity/JSONCompiler.cpp | 2 +- 11 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 libsolc/CMakeLists.txt rename solc/jsonCompiler.cpp => libsolc/libsolc.cpp (99%) rename solc/jsonCompiler.h => libsolc/libsolc.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 105de8515..8993f372c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,9 +43,10 @@ configure_project(TESTS) add_subdirectory(libdevcore) add_subdirectory(libevmasm) add_subdirectory(libsolidity) -add_subdirectory(solc) +add_subdirectory(libsolc) if (NOT EMSCRIPTEN) + add_subdirectory(solc) add_subdirectory(liblll) add_subdirectory(lllc) endif() diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt new file mode 100644 index 000000000..e67583dd2 --- /dev/null +++ b/libsolc/CMakeLists.txt @@ -0,0 +1,9 @@ +if (EMSCRIPTEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") + add_executable(soljson libsolc.cpp) + target_link_libraries(soljson PRIVATE solidity) +else() + add_library(libsolc libsolc.cpp) + set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc) + target_link_libraries(libsolc PRIVATE solidity) +endif() diff --git a/solc/jsonCompiler.cpp b/libsolc/libsolc.cpp similarity index 99% rename from solc/jsonCompiler.cpp rename to libsolc/libsolc.cpp index 23feaa2ae..3a6e15215 100644 --- a/solc/jsonCompiler.cpp +++ b/libsolc/libsolc.cpp @@ -20,7 +20,7 @@ * JSON interface for the solidity compiler to be used from Javascript. */ -#include +#include #include #include #include diff --git a/solc/jsonCompiler.h b/libsolc/libsolc.h similarity index 100% rename from solc/jsonCompiler.h rename to libsolc/libsolc.h diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index 8d4100bf2..557e3275d 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -40,7 +40,7 @@ TMPDIR=$(mktemp -d) if [[ "$SOLC_EMSCRIPTEN" = "On" ]] then - cp "$REPO_ROOT/build/solc/soljson.js" . + cp "$REPO_ROOT/build/libsolc/soljson.js" . npm install solc cat > solc < #include -#include +#include #include diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 7dc4808bd..0c904c778 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "../Metadata.h" #include "../TestHelper.h" From 3760284e1c040c93604ec9a86f11a9300617ad4b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 28 Nov 2017 15:00:32 +0100 Subject: [PATCH 035/186] Move-append for vector. --- libdevcore/CommonData.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index e76a09491..b85abe951 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -183,6 +183,12 @@ template std::vector& operator+=(std::vector& _a, U con _a.push_back(i); return _a; } +/// Concatenate the contents of a container onto a vector, move variant. +template std::vector& operator+=(std::vector& _a, U&& _b) +{ + std::move(_b.begin(), _b.end(), std::back_inserter(_a)); + return _a; +} /// Concatenate the contents of a container onto a set template std::set& operator+=(std::set& _a, U const& _b) { @@ -197,6 +203,17 @@ inline std::vector operator+(std::vector const& _a, std::vector const& ret += _b; return ret; } +/// Concatenate two vectors of elements, moving them. +template +inline std::vector operator+(std::vector&& _a, std::vector&& _b) +{ + std::vector ret(std::move(_a)); + if (&_a == &_b) + ret += ret; + else + ret += std::move(_b); + return ret; +} template bool contains(T const& _t, V const& _v) From 2add4f604b86c67da9f9b1616f2374b67f755878 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 11 Dec 2017 16:10:40 +0100 Subject: [PATCH 036/186] Description of the optimiser and stages. --- libjulia/optimiser/README.md | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 libjulia/optimiser/README.md diff --git a/libjulia/optimiser/README.md b/libjulia/optimiser/README.md new file mode 100644 index 000000000..771cb7071 --- /dev/null +++ b/libjulia/optimiser/README.md @@ -0,0 +1,61 @@ +## IULIA Optimiser + +The iulia optimiser consists of several stages and components that all transform +the AST in a semantically equivalent way. The goal is to end up either with code +that is shorter or at least only marginally longer but will allow further +optimisation steps. + +The optimiser currently follows a purely greedy strategy and does not do any +backtracking. + +## Disambiguator + +The disambiguator takes an AST and returns a fresh copy where all identifiers have +names unique to the input AST. This is a prerequisite for all other optimiser stages. +One of the benefits is that identifier lookup does not need to take scopes into account +and we can basically ignore the result of the analysis phase. + +All subsequent stages have the property that all names stay unique. This means if +a new identifier needs to be introduced, a new unique name is generated. + +## Function Hoister + +The function hoister moves all function definitions to the topmost block. This is +a semantically equivalent transformation as long as it is performed after the +disambiguation stage. The reason is that moving a definition upwards cannot decrease +its visibility and it is impossible to reference variables defined in a different function. + +The benefit of this stage is that function definitions can be lookup up more easily. + +## Function Grouper + +The function grouper has to be applied after the disambiguator and the function hoister. +Its effect is that all topmost elements that are not function definitions are moved +into a single block which is the first satement of the root block. + +After this step, a program has the following normal form: + + { I F... } + +Where I is a block that does not contain any function definitions (not even recursively) +and F is a list of function definitions such that no function contains a function definition. + +## Functional Inliner + +The functional inliner depends on the disambiguator, the function hoister and function grouper. +It performs function inlining such that the result of the inlining is an expression. This can +only be done if the body of the function to be inlined has the form ``{ r := E }`` where ``r`` +is the single return value of the function, ``E`` is an expression and all arguments in the +function call are so-called movable expressions. A movable expression is either a literal, a +variable or a function call (or EVM opcode) which does not have side-effects and also does not +depend on any side-effects. + +As an example, neither ``mload`` nor ``mstore`` would be allowed. + +## Full Function Inliner + +## Variable Eliminator + +## Unused Declaration Pruner + +## Function Unifier From d50e8abefb0631ff5892723d50611c63e76099c7 Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Fri, 20 Oct 2017 12:51:13 +0800 Subject: [PATCH 037/186] Document JSON I/O error classes --- docs/using-the-compiler.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index c12750c82..f1f13b82b 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -177,7 +177,8 @@ Output Description start: 0, end: 100 ], - // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc + // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc. + // See below for complete list of types. type: "TypeError", // Mandatory: Component where the error originated, such as "general", "ewasm", etc. component: "general", @@ -273,3 +274,21 @@ Output Description } } } + + +Error types +~~~~~~~~~~~ + +1. ``JSONError``: JSON input doesn't conform to the required format, e.g. input is not a JSON object, the language is not supported, etc. +2. ``IOError``: IO and import processing errors, such as unresolvable URL or hash mismatch in supplied sources. +3. ``ParserError``: Source code doesn't conform to the language rules. +4. ``DocstringParsingError``: The NatSpec tags in the comment block cannot be parsed. +5. ``SyntaxError``: Syntactical error, such as ``continue`` is used outside of a ``for`` loop. +6. ``DeclarationError``: Invalid, unresolvable or clashing identifier names. e.g. ``Identifier not found`` +7. ``TypeError``: Error within the type system, such as invalid type conversions, invalid assignments, etc. +8. ``UnimplementedFeatureError``: Feature is not supported by the compiler, but is expected to be supported in future versions. +9. ``InternalCompilerError``: Internal bug triggered in the compiler - this should be reported as an issue. +10. ``Exception``: Unknown failure during compilation - this should be reported as an issue. +11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. +12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. +13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. From 2f6f81640bca9fb358db12b088ff82f9b4f132bd Mon Sep 17 00:00:00 2001 From: Evgeny Medvedev Date: Tue, 14 Nov 2017 02:49:40 +0700 Subject: [PATCH 038/186] Add another contract with call to demonstrate re-entrancy vulnerability. Add another contract with call to demonstrate re-entrancy vulnerability as send explicitly sets gas to 2300 by default according to this commit 9ca7472 which makes it impossible to "get multiple refunds" because a non-zero CALL costs at least 9700 gas. This issue is discussed on Ethereum StackExchange https://ethereum.stackexchange.com/questions/30371/send-ether-reentrancy-attack-in-reality-how-could-fallback-function-make-a-mes/30616#30616 --- docs/security-considerations.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 337a3d3fb..197e80e5c 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -72,7 +72,24 @@ The problem is not too serious here because of the limited gas as part of ``send``, but it still exposes a weakness: Ether transfer always includes code execution, so the recipient could be a contract that calls back into ``withdraw``. This would let it get multiple refunds and -basically retrieve all the Ether in the contract. +basically retrieve all the Ether in the contract. In particular, the +following contract will allow an attacker to refund multiple times +as it uses ``call`` which forwards all remaining gas by default: + +:: + + pragma solidity ^0.4.0; + + // THIS CONTRACT CONTAINS A BUG - DO NOT USE + contract Fund { + /// Mapping of ether shares of the contract. + mapping(address => uint) shares; + /// Withdraw your share. + function withdraw() { + if (msg.sender.call.value(shares[msg.sender])()) + shares[msg.sender] = 0; + } + } To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as outlined further below: From 37b06884b26ec55fcc9bad2d6744b09dcf568006 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Dec 2017 03:31:30 +0000 Subject: [PATCH 039/186] Correct that ether transfers _can_ always include code execution in re-entrancy example --- docs/security-considerations.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 197e80e5c..1e2138fa3 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -69,8 +69,8 @@ complete contract): } The problem is not too serious here because of the limited gas as part -of ``send``, but it still exposes a weakness: Ether transfer always -includes code execution, so the recipient could be a contract that calls +of ``send``, but it still exposes a weakness: Ether transfer can always +include code execution, so the recipient could be a contract that calls back into ``withdraw``. This would let it get multiple refunds and basically retrieve all the Ether in the contract. In particular, the following contract will allow an attacker to refund multiple times From c6a4aba9f02e90fb51548fb7fbe0a0897f64b399 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 21 Nov 2017 13:01:59 +0200 Subject: [PATCH 040/186] Document function overloads and resolution --- docs/contracts.rst | 101 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 2b0956bb5..ca4e79c0f 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -426,12 +426,20 @@ value types and strings. bytes32 constant myHash = keccak256("abc"); } +.. index:: ! functions + +.. _functions: + +********* +Functions +********* + +.. index:: ! view function, function;view .. _view-functions: -************** View Functions -************** +============== Functions can be declared ``view`` in which case they promise not to modify the state. @@ -465,11 +473,12 @@ The following statements are considered modifying the state: .. warning:: The compiler does not enforce yet that a ``view`` method is not modifying state. +.. index:: ! pure function, function;pure + .. _pure-functions: -************** Pure Functions -************** +============== Functions can be declared ``pure`` in which case they promise not to read from or modify the state. @@ -498,9 +507,8 @@ In addition to the list of state modifying statements explained above, the follo .. _fallback-function: -***************** Fallback Function -***************** +================= A contract can have exactly one unnamed function. This function cannot have arguments and cannot return anything. @@ -577,6 +585,85 @@ Please ensure you test your fallback function thoroughly to ensure the execution } } +.. index:: ! overload + +.. _overload-function: + +Function Overloading +==================== + +A Contract can have multiple functions of the same name but with different arguments. +This also applies to inherited functions. The following example shows overloading of the +``f`` function in the scope of contract ``A``. + +:: + + pragma solidity ^0.4.16; + + contract A { + function f(uint _in) public pure returns (uint out) { + out = 1; + } + + function f(uint _in, bytes32 _key) public pure returns (uint out) { + out = 2; + } + } + +Overloaded functions are also present in the external interface. It is an error if two +externally visible functions differ by their Solidity types but not by their external types. + +:: + + // This will not compile + pragma solidity ^0.4.16; + + contract A { + function f(B _in) public pure returns (B out) { + out = _in; + } + + function f(address _in) public pure returns (address out) { + out = _in; + } + } + + contract B { + } + + +Both ``f`` function overloads above end up accepting the address type for the ABI although +they are considered different inside Solidity. + +Overload resolution and Argument matching +----------------------------------------- + +Overloaded functions are selected by matching the function declarations in the current scope +to the arguments supplied in the function call. Functions are selected as overload candidates +if all arguments can be implicitly converted to the expected types. If there is not exactly one +candidate, resolution fails. + +.. note:: + Return parameters are not taken into account for overload resolution. + +:: + + pragma solidity ^0.4.16; + + contract A { + function f(uint8 _in) public pure returns (uint8 out) { + out = _in; + } + + function f(uint256 _in) public pure returns (uint256 out) { + out = _in; + } + } + +Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8`` +and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly +converted to ``uint8``. + .. index:: ! event .. _events: @@ -854,7 +941,7 @@ derived override, but this function will bypass } If ``Base1`` calls a function of ``super``, it does not simply -call this function on one of its base contracts. Rather, it +call this function on one of its base contracts. Rather, it calls this function on the next base contract in the final inheritance graph, so it will call ``Base2.kill()`` (note that the final inheritance sequence is -- starting with the most From 2d171c25e57e821c242b2a6ffa9d6e890e8b207e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 Dec 2017 10:10:29 +0100 Subject: [PATCH 041/186] Limit number of secondary source locations. --- libsolidity/analysis/TypeChecker.cpp | 19 +++++-------------- libsolidity/interface/Exceptions.h | 12 ++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a578ad0f7..090e5159c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -171,13 +171,7 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con ssl.append("Another declaration is here:", (*it)->location()); string msg = "More than one constructor defined."; - size_t occurrences = ssl.infos.size(); - if (occurrences > 32) - { - ssl.infos.resize(32); - msg += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; - } - + ssl.limitSize(msg); m_errorReporter.declarationError( functions[_contract.name()].front()->location(), ssl, @@ -219,12 +213,7 @@ void TypeChecker::findDuplicateDefinitions(map> const& _defini if (ssl.infos.size() > 0) { - size_t occurrences = ssl.infos.size(); - if (occurrences > 32) - { - ssl.infos.resize(32); - _message += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; - } + ssl.limitSize(_message); m_errorReporter.declarationError( overloads[i]->location(), @@ -1679,10 +1668,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) SecondarySourceLocation ssl; for (auto function: contract->annotation().unimplementedFunctions) ssl.append("Missing implementation:", function->location()); + string msg = "Trying to create an instance of an abstract contract."; + ssl.limitSize(msg); m_errorReporter.typeError( _newExpression.location(), ssl, - "Trying to create an instance of an abstract contract." + msg ); } if (!contract->constructorIsPublic()) diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 09301b10b..7c66d5721 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -109,6 +109,18 @@ public: infos.push_back(std::make_pair(_errMsg, _sourceLocation)); return *this; } + /// Limits the number of secondary source locations to 32 and appends a notice to the + /// error message. + void limitSize(std::string& _message) + { + size_t occurrences = infos.size(); + if (occurrences > 32) + { + infos.resize(32); + _message += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; + } + } + std::vector infos; }; From 5226d54ed16bb7247c64ac67fec786301a3210ec Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 12:33:11 +0000 Subject: [PATCH 042/186] Improve error message for constant evaluator --- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/ast/Types.h | 2 +- .../SolidityNameAndTypeResolution.cpp | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f22c95cc1..d8030b97f 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -150,7 +150,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) ConstantEvaluator e(*length, m_errorReporter); auto const* lengthType = dynamic_cast(length->annotation().type.get()); if (!lengthType || !lengthType->mobileType()) - fatalTypeError(length->location(), "Invalid array length, expected integer literal."); + fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); else if (lengthType->isFractional()) fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 635279abe..a54e4e099 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -257,7 +257,7 @@ public: } virtual u256 literalValue(Literal const*) const { - solAssert(false, "Literal value requested for type without literals."); + solAssert(false, "Literal value requested for type without literals: " + toString(false)); } /// @returns a (simpler) type that is encoded in the same way for external function calls. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 39dba0dea..d141361fc 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2109,7 +2109,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) function f(uint a) public { uint8[a] x; } } )"; - CHECK_ERROR(text, TypeError, "Identifier must be declared constant."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_with_negative_length) @@ -4398,7 +4398,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) @@ -4410,7 +4410,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) @@ -7254,7 +7254,7 @@ BOOST_AUTO_TEST_CASE(array_length_too_large) uint[8**90] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) @@ -7264,7 +7264,7 @@ BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) uint[true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_constant_var) @@ -7286,7 +7286,7 @@ BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) @@ -7297,7 +7297,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) uint[f] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_can_be_recursive_constant) @@ -7321,7 +7321,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_function_call) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_const_cannot_be_fractional) @@ -7409,7 +7409,7 @@ BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_invalid_expression) From 7ff9a85592cbb50b7a72654849582c780d7fc494 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 12:41:33 +0000 Subject: [PATCH 043/186] Reduce the types of errors outputted by ConstantEvaluator --- libsolidity/analysis/ConstantEvaluator.cpp | 14 ++++++-------- test/libsolidity/SolidityNameAndTypeResolution.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 4d546e68d..7f5ffb51d 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -33,7 +33,7 @@ void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypePointer const& subType = _operation.subExpression().annotation().type; if (!dynamic_cast(subType.get())) - m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression."); + return; TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); _operation.annotation().type = t; } @@ -44,9 +44,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) TypePointer const& leftType = _operation.leftExpression().annotation().type; TypePointer const& rightType = _operation.rightExpression().annotation().type; if (!dynamic_cast(leftType.get())) - m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression."); + return; if (!dynamic_cast(rightType.get())) - m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression."); + return; TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); if (!commonType) { @@ -71,8 +71,6 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) void ConstantEvaluator::endVisit(Literal const& _literal) { _literal.annotation().type = Type::forLiteral(_literal); - if (!_literal.annotation().type) - m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); } void ConstantEvaluator::endVisit(Identifier const& _identifier) @@ -81,11 +79,11 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) if (!variableDeclaration) return; if (!variableDeclaration->isConstant()) - m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant."); + return; - ASTPointer value = variableDeclaration->value(); + ASTPointer const& value = variableDeclaration->value(); if (!value) - m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value."); + return; if (!value->annotation().type) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index d141361fc..b4e4c56eb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7419,25 +7419,25 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) uint[-true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[true/1] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[1/true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[1.111111E1111111111111] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid literal value."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[3/0] ids; From 48c7ba72f3616a037fe3fe8cf1a490b121c416e3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 22 Nov 2017 12:54:23 +0100 Subject: [PATCH 044/186] Simplify ConstantEvaluator. --- libsolidity/analysis/ConstantEvaluator.cpp | 77 +++++++++++-------- libsolidity/analysis/ConstantEvaluator.h | 18 ++++- libsolidity/analysis/ReferencesResolver.cpp | 7 +- .../SolidityNameAndTypeResolution.cpp | 2 +- 4 files changed, 62 insertions(+), 42 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 7f5ffb51d..14171b01b 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -28,49 +28,42 @@ using namespace std; using namespace dev; using namespace dev::solidity; -/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { - TypePointer const& subType = _operation.subExpression().annotation().type; - if (!dynamic_cast(subType.get())) - return; - TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); - _operation.annotation().type = t; + auto sub = type(_operation.subExpression()); + if (sub) + setType(_operation, sub->unaryOperatorResult(_operation.getOperator())); } -/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { - TypePointer const& leftType = _operation.leftExpression().annotation().type; - TypePointer const& rightType = _operation.rightExpression().annotation().type; - if (!dynamic_cast(leftType.get())) - return; - if (!dynamic_cast(rightType.get())) - return; - TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); - if (!commonType) + auto left = type(_operation.leftExpression()); + auto right = type(_operation.rightExpression()); + if (left && right) { - m_errorReporter.typeError( - _operation.location(), - "Operator " + - string(Token::toString(_operation.getOperator())) + - " not compatible with types " + - leftType->toString() + - " and " + - rightType->toString() + auto commonType = left->binaryOperatorResult(_operation.getOperator(), right); + if (!commonType) + m_errorReporter.fatalTypeError( + _operation.location(), + "Operator " + + string(Token::toString(_operation.getOperator())) + + " not compatible with types " + + left->toString() + + " and " + + right->toString() + ); + setType( + _operation, + Token::isCompareOp(_operation.getOperator()) ? + make_shared() : + left->binaryOperatorResult(_operation.getOperator(), right) ); - commonType = leftType; } - _operation.annotation().commonType = commonType; - _operation.annotation().type = - Token::isCompareOp(_operation.getOperator()) ? - make_shared() : - commonType; } void ConstantEvaluator::endVisit(Literal const& _literal) { - _literal.annotation().type = Type::forLiteral(_literal); + setType(_literal, Type::forLiteral(_literal)); } void ConstantEvaluator::endVisit(Identifier const& _identifier) @@ -84,13 +77,29 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) ASTPointer const& value = variableDeclaration->value(); if (!value) return; - - if (!value->annotation().type) + else if (!m_types->count(value.get())) { if (m_depth > 32) m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); - ConstantEvaluator e(*value, m_errorReporter, m_depth + 1); + ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value); } - _identifier.annotation().type = value->annotation().type; + setType(_identifier, type(*value)); +} + +void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type) +{ + if (_type && _type->category() == Type::Category::RationalNumber) + (*m_types)[&_node] = _type; +} + +TypePointer ConstantEvaluator::type(ASTNode const& _node) +{ + return (*m_types)[&_node]; +} + +TypePointer ConstantEvaluator::evaluate(Expression const& _expr) +{ + _expr.accept(*this); + return type(_expr); } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 6725d610f..77a357b65 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -38,22 +38,32 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0): + ConstantEvaluator( + ErrorReporter& _errorReporter, + size_t _newDepth = 0, + std::shared_ptr> _types = std::make_shared>() + ): m_errorReporter(_errorReporter), - m_depth(_newDepth) + m_depth(_newDepth), + m_types(_types) { - _expr.accept(*this); } + TypePointer evaluate(Expression const& _expr); + private: virtual void endVisit(BinaryOperation const& _operation); virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(Literal const& _literal); virtual void endVisit(Identifier const& _identifier); + void setType(ASTNode const& _node, TypePointer const& _type); + TypePointer type(ASTNode const& _node); + ErrorReporter& m_errorReporter; /// Current recursion depth. - size_t m_depth; + size_t m_depth = 0; + std::shared_ptr> m_types; }; } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index d8030b97f..9eee16af4 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -146,9 +146,10 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); if (Expression const* length = _typeName.length()) { - if (!length->annotation().type) - ConstantEvaluator e(*length, m_errorReporter); - auto const* lengthType = dynamic_cast(length->annotation().type.get()); + TypePointer lengthTypeGeneric = length->annotation().type; + if (!lengthTypeGeneric) + lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length); + RationalNumberType const* lengthType = dynamic_cast(lengthTypeGeneric.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); else if (lengthType->isFractional()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b4e4c56eb..8f58dcb15 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7370,7 +7370,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter) } } )"; - CHECK_ERROR(text, TypeError, "Constant identifier declaration must have a constant value."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant) From e7ed9d878e80e53da9d4cc603e6527918c5a432a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 Dec 2017 10:45:40 +0100 Subject: [PATCH 045/186] Re-use `commonType` --- libsolidity/analysis/ConstantEvaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 14171b01b..83f37f474 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -56,7 +56,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) _operation, Token::isCompareOp(_operation.getOperator()) ? make_shared() : - left->binaryOperatorResult(_operation.getOperator(), right) + commonType ); } } From a64c1e54874fd8f14da4f582acd787d57b687878 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Dec 2017 10:34:24 +0000 Subject: [PATCH 046/186] Update changelog for some 0.4.20 changes --- Changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 4cd83eeb2..e2325a0cd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,11 @@ ### 0.4.20 (unreleased) Features: + * Limit the number of warnings raised for creating abstract contracts. * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions). Bugfixes: + * Parser: Disallow event declarations with no parameter list. * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters (instead of an internal compiler error). @@ -20,7 +22,6 @@ Features: * Type Checker: More detailed errors for invalid array lengths (such as division by zero). Bugfixes: - * Parser: Disallow event declarations with no parameter list. ### 0.4.18 (2017-10-18) From 6e521d59b0a30fa0673aaf84559d5b74dbb1eed7 Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Tue, 12 Dec 2017 18:47:30 +0000 Subject: [PATCH 047/186] Fix Solidity warnings --- docs/abi-spec.rst | 14 +-- docs/assembly.rst | 18 +-- docs/common-patterns.rst | 18 ++- docs/contracts.rst | 150 +++++++++++++---------- docs/control-structures.rst | 58 +++++---- docs/frequently-asked-questions.rst | 34 ++--- docs/introduction-to-smart-contracts.rst | 12 +- docs/security-considerations.rst | 18 +-- docs/solidity-by-example.rst | 42 ++++--- docs/structure-of-a-contract.rst | 6 +- docs/style-guide.rst | 81 ++++++------ docs/types.rst | 88 ++++++------- docs/units-and-global-variables.rst | 2 +- 13 files changed, 291 insertions(+), 250 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index e968fb063..e106d98b2 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -190,9 +190,9 @@ Given the contract: pragma solidity ^0.4.0; contract Foo { - function bar(bytes3[2] xy) {} - function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; } - function sam(bytes name, bool z, uint[] data) {} + function bar(bytes3[2] xy) public {} + function baz(uint32 x, bool y) public returns (bool r) { r = x > 32 || y; } + function sam(bytes name, bool z, uint[] data) public {} } @@ -333,10 +333,10 @@ For example, pragma solidity ^0.4.0; contract Test { - function Test(){ b = 0x12345678901234567890123456789012; } + function Test() public { b = 0x12345678901234567890123456789012; } event Event(uint indexed a, bytes32 b); event Event2(uint indexed a, bytes32 b); - function foo(uint a) { Event(a, b); } + function foo(uint a) public { Event(a, b); } bytes32 b; } @@ -383,8 +383,8 @@ As an example, the code contract Test { struct S { uint a; uint[] b; T[] c; } struct T { uint x; uint y; } - function f(S s, T t, uint a) { } - function g() returns (S s, T t, uint a) {} + function f(S s, T t, uint a) public { } + function g() public returns (S s, T t, uint a) {} } would result in the JSON: diff --git a/docs/assembly.rst b/docs/assembly.rst index a4fa88c63..e09a556b3 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -23,7 +23,7 @@ arising when writing manual assembly by the following features: * functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul`` * assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)`` -* access to external variables: ``function f(uint x) { assembly { x := sub(x, 1) } }`` +* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }`` * labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))`` * loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }`` * if statements: ``if slt(x, 0) { x := sub(0, x) }`` @@ -54,7 +54,7 @@ idea is that assembly libraries will be used to enhance the language in such way pragma solidity ^0.4.0; library GetCode { - function at(address _addr) returns (bytes o_code) { + function at(address _addr) public returns (bytes o_code) { assembly { // retrieve the size of the code, this needs assembly let size := extcodesize(_addr) @@ -83,7 +83,7 @@ you really know what you are doing. library VectorSum { // This function is less efficient because the optimizer currently fails to // remove the bounds checks in array access. - function sumSolidity(uint[] _data) returns (uint o_sum) { + function sumSolidity(uint[] _data) public returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) o_sum += _data[i]; } @@ -91,7 +91,7 @@ you really know what you are doing. // We know that we only access the array in bounds, so we can avoid the check. // 0x20 needs to be added to an array because the first slot contains the // array length. - function sumAsm(uint[] _data) returns (uint o_sum) { + function sumAsm(uint[] _data) returns (uint o_sum) public { for (uint i = 0; i < _data.length; ++i) { assembly { o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20)))) @@ -100,7 +100,7 @@ you really know what you are doing. } // Same as above, but accomplish the entire code within inline assembly. - function sumPureAsm(uint[] _data) returns (uint o_sum) { + function sumPureAsm(uint[] _data) returns (uint o_sum) public { assembly { // Load the length (first 32 bytes) let len := mload(_data) @@ -388,7 +388,7 @@ changes during the call, and thus references to local variables will be wrong. contract C { uint b; - function f(uint x) returns (uint r) { + function f(uint x) public returns (uint r) { assembly { r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero } @@ -462,7 +462,7 @@ be just ``0``, but it can also be a complex functional-style expression. pragma solidity ^0.4.0; contract C { - function f(uint x) returns (uint b) { + function f(uint x) public returns (uint b) { assembly { let v := add(x, 1) mstore(0x80, v) @@ -574,7 +574,7 @@ Simply leave the initialization and post-iteration parts empty. x := add(x, mload(i)) i := add(i, 0x20) } - } + } Functions --------- @@ -713,7 +713,7 @@ We consider the runtime bytecode of the following Solidity program:: pragma solidity ^0.4.0; contract C { - function f(uint x) returns (uint y) { + function f(uint x) public returns (uint y) { y = 1; for (uint i = 0; i < x; i++) y = 2 * y; diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 52319be0d..7e09f5346 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -36,12 +36,12 @@ become the new richest. mapping (address => uint) pendingWithdrawals; - function WithdrawalContract() payable { + function WithdrawalContract() public payable { richest = msg.sender; mostSent = msg.value; } - function becomeRichest() payable returns (bool) { + function becomeRichest() public payable returns (bool) { if (msg.value > mostSent) { pendingWithdrawals[richest] += msg.value; richest = msg.sender; @@ -52,7 +52,7 @@ become the new richest. } } - function withdraw() { + function withdraw() public { uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks @@ -71,12 +71,12 @@ This is as opposed to the more intuitive sending pattern: address public richest; uint public mostSent; - function SendContract() payable { + function SendContract() public payable { richest = msg.sender; mostSent = msg.value; } - function becomeRichest() payable returns (bool) { + function becomeRichest() public payable returns (bool) { if (msg.value > mostSent) { // This line can cause problems (explained below). richest.transfer(msg.value); @@ -157,6 +157,7 @@ restrictions highly readable. /// Make `_newOwner` the new owner of this /// contract. function changeOwner(address _newOwner) + public onlyBy(owner) { owner = _newOwner; @@ -171,6 +172,7 @@ restrictions highly readable. /// May only be called 6 weeks after /// the contract has been created. function disown() + public onlyBy(owner) onlyAfter(creationTime + 6 weeks) { @@ -191,6 +193,7 @@ restrictions highly readable. } function forceOwnerChange(address _newOwner) + public costs(200 ether) { owner = _newOwner; @@ -310,6 +313,7 @@ function finishes. // Order of the modifiers matters here! function bid() + public payable timedTransitions atStage(Stages.AcceptingBlindedBids) @@ -318,6 +322,7 @@ function finishes. } function reveal() + public timedTransitions atStage(Stages.RevealBids) { @@ -332,6 +337,7 @@ function finishes. } function g() + public timedTransitions atStage(Stages.AnotherStage) transitionNext @@ -339,6 +345,7 @@ function finishes. } function h() + public timedTransitions atStage(Stages.AreWeDoneYet) transitionNext @@ -346,6 +353,7 @@ function finishes. } function i() + public timedTransitions atStage(Stages.Finished) { diff --git a/docs/contracts.rst b/docs/contracts.rst index ca4e79c0f..a0a3009ba 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible. // This is the constructor which registers the // creator and the assigned name. - function OwnedToken(bytes32 _name) { + function OwnedToken(bytes32 _name) public { // State variables are accessed via their name // and not via e.g. this.owner. This also applies // to functions and especially in the constructors, @@ -67,7 +67,7 @@ This means that cyclic creation dependencies are impossible. name = _name; } - function changeName(bytes32 newName) { + function changeName(bytes32 newName) public { // Only the creator can alter the name -- // the comparison is possible since contracts // are implicitly convertible to addresses. @@ -75,7 +75,7 @@ This means that cyclic creation dependencies are impossible. name = newName; } - function transfer(address newOwner) { + function transfer(address newOwner) public { // Only the current owner can transfer the token. if (msg.sender != owner) return; // We also want to ask the creator if the transfer @@ -90,6 +90,7 @@ This means that cyclic creation dependencies are impossible. contract TokenCreator { function createToken(bytes32 name) + public returns (OwnedToken tokenAddress) { // Create a new Token contract and return its address. @@ -99,16 +100,17 @@ This means that cyclic creation dependencies are impossible. return new OwnedToken(name); } - function changeName(OwnedToken tokenAddress, bytes32 name) { + function changeName(OwnedToken tokenAddress, bytes32 name) public { // Again, the external type of "tokenAddress" is // simply "address". tokenAddress.changeName(name); } - function isTokenTransferOK( - address currentOwner, - address newOwner - ) returns (bool ok) { + function isTokenTransferOK(address currentOwner, address newOwner) + public + view + returns (bool ok) + { // Check some arbitrary condition. address tokenAddress = msg.sender; return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); @@ -171,10 +173,10 @@ return parameter list for functions. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { - function f(uint a) private returns (uint b) { return a + 1; } + function f(uint a) private pure returns (uint b) { return a + 1; } function setData(uint a) internal { data = a; } uint public data; } @@ -193,13 +195,13 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value uint private data; function f(uint a) private returns(uint b) { return a + 1; } - function setData(uint a) { data = a; } + function setData(uint a) public { data = a; } function getData() public returns(uint) { return data; } function compute(uint a, uint b) internal returns (uint) { return a+b; } } contract D { - function readData() { + function readData() public { C c = new C(); uint local = c.f(7); // error: member "f" is not visible c.setData(3); @@ -209,7 +211,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value } contract E is C { - function g() { + function g() public { C c = new C(); uint val = compute(3, 5); // access to internal member (from derived to parent contract) } @@ -238,7 +240,7 @@ be done at declaration. contract Caller { C c = new C(); - function f() { + function f() public { uint local = c.data(); } } @@ -254,7 +256,7 @@ it is evaluated as a state variable. If it is accessed externally contract C { uint public data; - function x() { + function x() public { data = 3; // internal access uint val = this.data(); // external access } @@ -277,7 +279,7 @@ The next example is a bit more complex: It will generate a function of the following form:: - function data(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b) { + function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { a = data[arg1][arg2][arg3].a; b = data[arg1][arg2][arg3].b; } @@ -302,7 +304,7 @@ inheritable properties of contracts and may be overridden by derived contracts. pragma solidity ^0.4.11; contract owned { - function owned() { owner = msg.sender; } + function owned() public { owner = msg.sender; } address owner; // This contract only defines a modifier but does not use @@ -323,7 +325,7 @@ inheritable properties of contracts and may be overridden by derived contracts. // "owned" and applies it to the "close"-function, which // causes that calls to "close" only have an effect if // they are made by the stored owner. - function close() onlyOwner { + function close() public onlyOwner { selfdestruct(owner); } } @@ -341,16 +343,16 @@ inheritable properties of contracts and may be overridden by derived contracts. mapping (address => bool) registeredAddresses; uint price; - function Register(uint initialPrice) { price = initialPrice; } + function Register(uint initialPrice) public { price = initialPrice; } // It is important to also provide the // "payable" keyword here, otherwise the function will // automatically reject all Ether sent to it. - function register() payable costs(price) { + function register() public payable costs(price) { registeredAddresses[msg.sender] = true; } - function changePrice(uint _price) onlyOwner { + function changePrice(uint _price) public onlyOwner { price = _price; } } @@ -368,7 +370,7 @@ inheritable properties of contracts and may be overridden by derived contracts. /// reentrant calls from within msg.sender.call cannot call f again. /// The `return 7` statement assigns 7 to the return value but still /// executes the statement `locked = false` in the modifier. - function f() noReentrancy returns (uint) { + function f() public noReentrancy returns (uint) { require(msg.sender.call()); return 7; } @@ -459,7 +461,7 @@ The following statements are considered modifying the state: pragma solidity ^0.4.16; contract C { - function f(uint a, uint b) view returns (uint) { + function f(uint a, uint b) public view returns (uint) { return a * (b + 42) + now; } } @@ -495,7 +497,7 @@ In addition to the list of state modifying statements explained above, the follo pragma solidity ^0.4.16; contract C { - function f(uint a, uint b) pure returns (uint) { + function f(uint a, uint b) public pure returns (uint) { return a * (b + 42); } } @@ -561,7 +563,7 @@ Please ensure you test your fallback function thoroughly to ensure the execution // Sending Ether to this contract will cause an exception, // because the fallback function does not have the "payable" // modifier. - function() { x = 1; } + function() public { x = 1; } uint x; } @@ -569,11 +571,11 @@ Please ensure you test your fallback function thoroughly to ensure the execution // This contract keeps all Ether sent to it with no way // to get it back. contract Sink { - function() payable { } + function() public payable { } } contract Caller { - function callTest(Test test) { + function callTest(Test test) public { test.call(0xabcdef01); // hash does not exist // results in test.x becoming == 1. @@ -597,7 +599,7 @@ This also applies to inherited functions. The following example shows overloadin ``f`` function in the scope of contract ``A``. :: - + pragma solidity ^0.4.16; contract A { @@ -721,7 +723,7 @@ All non-indexed arguments will be stored in the data part of the log. uint _value ); - function deposit(bytes32 _id) payable { + function deposit(bytes32 _id) public payable { // Any call to this function (even deeply nested) can // be detected from the JavaScript API by filtering // for `Deposit` to be called. @@ -770,7 +772,7 @@ as topics. The event call above can be performed in the same way as pragma solidity ^0.4.10; contract C { - function f() { + function f() public payable { bytes32 _id = 0x420042; log3( bytes32(msg.value), @@ -814,7 +816,7 @@ Details are given in the following example. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract owned { function owned() { owner = msg.sender; } @@ -836,12 +838,12 @@ Details are given in the following example. // without body. If a contract does not implement all // functions it can only be used as an interface. contract Config { - function lookup(uint id) returns (address adr); + function lookup(uint id) public returns (address adr); } contract NameReg { - function register(bytes32 name); - function unregister(); + function register(bytes32 name) public; + function unregister() public; } // Multiple inheritance is possible. Note that "owned" is @@ -849,7 +851,7 @@ Details are given in the following example. // instance of "owned" (as for virtual inheritance in C++). contract named is owned, mortal { function named(bytes32 name) { - Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970); + Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).register(name); } @@ -858,9 +860,9 @@ Details are given in the following example. // types of output parameters, that causes an error. // Both local and message-based function calls take these overrides // into account. - function kill() { + function kill() public { if (msg.sender == owner) { - Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970); + Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).unregister(); // It is still possible to call a specific // overridden function. @@ -873,11 +875,11 @@ Details are given in the following example. // provided in the header (or modifier-invocation-style at // the constructor of the derived contract (see below)). contract PriceFeed is owned, mortal, named("GoldFeed") { - function updateInfo(uint newInfo) { + function updateInfo(uint newInfo) public { if (msg.sender == owner) info = newInfo; } - function get() constant returns(uint r) { return info; } + function get() public view returns(uint r) { return info; } uint info; } @@ -889,22 +891,22 @@ seen in the following example:: pragma solidity ^0.4.0; contract owned { - function owned() { owner = msg.sender; } + function owned() public { owner = msg.sender; } address owner; } contract mortal is owned { - function kill() { + function kill() public { if (msg.sender == owner) selfdestruct(owner); } } contract Base1 is mortal { - function kill() { /* do cleanup 1 */ mortal.kill(); } + function kill() public { /* do cleanup 1 */ mortal.kill(); } } contract Base2 is mortal { - function kill() { /* do cleanup 2 */ mortal.kill(); } + function kill() public { /* do cleanup 2 */ mortal.kill(); } } contract Final is Base1, Base2 { @@ -918,23 +920,23 @@ derived override, but this function will bypass pragma solidity ^0.4.0; contract owned { - function owned() { owner = msg.sender; } + function owned() public { owner = msg.sender; } address owner; } contract mortal is owned { - function kill() { + function kill() public { if (msg.sender == owner) selfdestruct(owner); } } contract Base1 is mortal { - function kill() { /* do cleanup 1 */ super.kill(); } + function kill() public { /* do cleanup 1 */ super.kill(); } } contract Base2 is mortal { - function kill() { /* do cleanup 2 */ super.kill(); } + function kill() public { /* do cleanup 2 */ super.kill(); } } contract Final is Base2, Base1 { @@ -963,11 +965,11 @@ the base constructors. This can be done in two ways:: contract Base { uint x; - function Base(uint _x) { x = _x; } + function Base(uint _x) public { x = _x; } } contract Derived is Base(7) { - function Derived(uint _y) Base(_y * _y) { + function Derived(uint _y) Base(_y * _y) public { } } @@ -1032,7 +1034,7 @@ Contract functions can lack an implementation as in the following example (note pragma solidity ^0.4.0; contract Feline { - function utterance() returns (bytes32); + function utterance() public returns (bytes32); } Such contracts cannot be compiled (even if they contain @@ -1042,11 +1044,11 @@ but they can be used as base contracts:: pragma solidity ^0.4.0; contract Feline { - function utterance() returns (bytes32); + function utterance() public returns (bytes32); } contract Cat is Feline { - function utterance() returns (bytes32) { return "miaow"; } + function utterance() public returns (bytes32) { return "miaow"; } } If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. @@ -1077,7 +1079,7 @@ Interfaces are denoted by their own keyword: pragma solidity ^0.4.11; interface Token { - function transfer(address recipient, uint amount); + function transfer(address recipient, uint amount) public; } Contracts can inherit interfaces as they would inherit other contracts. @@ -1120,7 +1122,7 @@ more advanced example to implement a set). :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.16; library Set { // We define a new struct datatype that will be used to @@ -1134,6 +1136,7 @@ more advanced example to implement a set). // to call the first parameter 'self', if the function can // be seen as a method of that object. function insert(Data storage self, uint value) + public returns (bool) { if (self.flags[value]) @@ -1143,6 +1146,7 @@ more advanced example to implement a set). } function remove(Data storage self, uint value) + public returns (bool) { if (!self.flags[value]) @@ -1152,6 +1156,8 @@ more advanced example to implement a set). } function contains(Data storage self, uint value) + public + view returns (bool) { return self.flags[value]; @@ -1161,7 +1167,7 @@ more advanced example to implement a set). contract C { Set.Data knownValues; - function register(uint value) { + function register(uint value) public { // The library functions can be called without a // specific instance of the library, since the // "instance" will be the current contract. @@ -1190,19 +1196,19 @@ custom types without the overhead of external function calls: :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; library BigInt { struct bigint { uint[] limbs; } - function fromUint(uint x) internal returns (bigint r) { + function fromUint(uint x) internal pure returns (bigint r) { r.limbs = new uint[](1); r.limbs[0] = x; } - function add(bigint _a, bigint _b) internal returns (bigint r) { + function add(bigint _a, bigint _b) internal pure returns (bigint r) { r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); uint carry = 0; for (uint i = 0; i < r.limbs.length; ++i) { @@ -1224,11 +1230,11 @@ custom types without the overhead of external function calls: } } - function limb(bigint _a, uint _limb) internal returns (uint) { + function limb(bigint _a, uint _limb) internal pure returns (uint) { return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; } - function max(uint a, uint b) private returns (uint) { + function max(uint a, uint b) private pure returns (uint) { return a > b ? a : b; } } @@ -1236,7 +1242,7 @@ custom types without the overhead of external function calls: contract C { using BigInt for BigInt.bigint; - function f() { + function f() public pure { var x = BigInt.fromUint(7); var y = BigInt.fromUint(uint(-1)); var z = x.add(y); @@ -1294,13 +1300,14 @@ available without having to add further code. Let us rewrite the set example from the :ref:`libraries` in this way:: - pragma solidity ^0.4.11; + pragma solidity ^0.4.16; // This is the same code as before, just without comments library Set { struct Data { mapping(uint => bool) flags; } function insert(Data storage self, uint value) + public returns (bool) { if (self.flags[value]) @@ -1310,6 +1317,7 @@ Let us rewrite the set example from the } function remove(Data storage self, uint value) + public returns (bool) { if (!self.flags[value]) @@ -1319,6 +1327,8 @@ Let us rewrite the set example from the } function contains(Data storage self, uint value) + public + view returns (bool) { return self.flags[value]; @@ -1329,7 +1339,7 @@ Let us rewrite the set example from the using Set for Set.Data; // this is the crucial change Set.Data knownValues; - function register(uint value) { + function register(uint value) public { // Here, all variables of type Set.Data have // corresponding member functions. // The following function call is identical to @@ -1340,10 +1350,14 @@ Let us rewrite the set example from the It is also possible to extend elementary types in that way:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; library Search { - function indexOf(uint[] storage self, uint value) returns (uint) { + function indexOf(uint[] storage self, uint value) + public + view + returns (uint) + { for (uint i = 0; i < self.length; i++) if (self[i] == value) return i; return uint(-1); @@ -1354,11 +1368,11 @@ It is also possible to extend elementary types in that way:: using Search for uint[]; uint[] data; - function append(uint value) { + function append(uint value) public { data.push(value); } - function replace(uint _old, uint _new) { + function replace(uint _old, uint _new) public { // This performs the library function call uint index = data.indexOf(_old); if (index == uint(-1)) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index bcb597cf1..079b14c75 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -20,10 +20,10 @@ For example, suppose we want our contract to accept one kind of external calls with two integers, we would write something like:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract Simple { - function taker(uint _a, uint _b) { + function taker(uint _a, uint _b) public pure { // do something with _a and _b. } } @@ -36,10 +36,14 @@ The output parameters can be declared with the same syntax after the the sum and the product of the two given integers, then we would write:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract Simple { - function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) { + function arithmetics(uint _a, uint _b) + public + pure + returns (uint o_sum, uint o_product) + { o_sum = _a + _b; o_product = _a * _b; } @@ -95,11 +99,11 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { - function g(uint a) returns (uint ret) { return f(); } - function f() returns (uint ret) { return g(7) + f(); } + function g(uint a) public pure returns (uint ret) { return f(); } + function f() internal pure returns (uint ret) { return g(7) + f(); } } These function calls are translated into simple jumps inside the EVM. This has @@ -125,13 +129,13 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe pragma solidity ^0.4.0; contract InfoFeed { - function info() payable returns (uint ret) { return 42; } + function info() public payable returns (uint ret) { return 42; } } contract Consumer { InfoFeed feed; - function setFeed(address addr) { feed = InfoFeed(addr); } - function callFeed() { feed.info.value(10).gas(800)(); } + function setFeed(address addr) public { feed = InfoFeed(addr); } + function public callFeed() { feed.info.value(10).gas(800)(); } } The modifier ``payable`` has to be used for ``info``, because otherwise, the `.value()` @@ -180,11 +184,11 @@ parameters from the function declaration, but can be in arbitrary order. pragma solidity ^0.4.0; contract C { - function f(uint key, uint value) { + function f(uint key, uint value) public { // ... } - function g() { + function g() public { // named arguments f({value: 2, key: 3}); } @@ -198,11 +202,11 @@ Those parameters will still be present on the stack, but they are inaccessible. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { // omitted name for parameter - function func(uint k, uint) returns(uint) { + function func(uint k, uint) public pure returns(uint) { return k; } } @@ -225,7 +229,7 @@ creation-dependencies are not possible. contract D { uint x; - function D(uint a) payable { + function D(uint a) public payable { x = a; } } @@ -233,11 +237,11 @@ creation-dependencies are not possible. contract C { D d = new D(4); // will be executed as part of C's constructor - function createD(uint arg) { + function createD(uint arg) public { D newD = new D(arg); } - function createAndEndowD(uint arg, uint amount) payable { + function createAndEndowD(uint arg, uint amount) public payable { // Send ether along with the creation D newD = (new D).value(amount)(arg); } @@ -270,16 +274,16 @@ Destructuring Assignments and Returning Multiple Values Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { uint[] data; - function f() returns (uint, bool, uint) { + function f() public pure returns (uint, bool, uint) { return (7, true, 2); } - function g() { + function g() public { // Declares and assigns the variables. Specifying the type explicitly is not possible. var (x, b, y) = f(); // Assigns to a pre-existing variable. @@ -326,10 +330,10 @@ As a result, the following code is illegal and cause the compiler to throw an er // This will not compile - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract ScopingErrors { - function scoping() { + function scoping() public { uint i = 0; while (i++ < 1) { @@ -341,7 +345,7 @@ As a result, the following code is illegal and cause the compiler to throw an er } } - function minimalScoping() { + function minimalScoping() public { { uint same2 = 0; } @@ -351,7 +355,7 @@ As a result, the following code is illegal and cause the compiler to throw an er } } - function forLoopScoping() { + function forLoopScoping() public { for (uint same3 = 0; same3 < 1; same3++) { } @@ -364,9 +368,9 @@ In addition to this, if a variable is declared, it will be initialized at the be As a result, the following code is legal, despite being poorly written:: pragma solidity ^0.4.0; - + contract C { - function foo() returns (uint) { + function foo() public pure returns (uint) { // baz is implicitly initialized as 0 uint bar = 5; if (true) { @@ -412,7 +416,7 @@ and how ``assert`` can be used for internal error checking:: pragma solidity ^0.4.0; contract Sharer { - function sendHalf(address addr) payable returns (uint balance) { + function sendHalf(address addr) public payable returns (uint balance) { require(msg.value % 2 == 0); // Only allow even numbers uint balanceBeforeTransfer = this.balance; addr.transfer(msg.value / 2); diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 5c427c692..5343b8485 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -111,10 +111,10 @@ array in the return statement. Pretty cool, huh? Example:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { - function f() returns (uint8[5]) { + function f() public pure returns (uint8[5]) { string[4] memory adaArr = ["This", "is", "an", "array"]; return ([1, 2, 3, 4, 5]); } @@ -190,11 +190,11 @@ you should always convert it to a ``bytes`` first:: contract C { string s; - function append(byte c) { + function append(byte c) public { bytes(s).push(c); } - function set(uint i, byte c) { + function set(uint i, byte c) public { bytes(s)[i] = c; } } @@ -232,12 +232,14 @@ situation. If you do not want to throw, you can return a pair:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { uint[] counters; function getCounter(uint index) + public + view returns (uint counter, bool error) { if (index >= counters.length) return (0, true); @@ -245,7 +247,7 @@ If you do not want to throw, you can return a pair:: return (counters[index], false); } - function checkCounter(uint index) { + function checkCounter(uint index) public view { var (counter, error) = getCounter(index); if (error) { // ... @@ -316,11 +318,11 @@ Example:: uint[] data1; uint[] data2; - function appendOne() { + function appendOne() public { append(data1); } - function appendTwo() { + function appendTwo() public { append(data2); } @@ -349,7 +351,7 @@ be created in memory, although it will be created in storage:: uint someVariable; uint[] data; - function f() { + function f() public { uint[] x; x.push(2); data = x; @@ -375,7 +377,7 @@ The correct way to do this is the following:: uint someVariable; uint[] data; - function f() { + function f() public { uint[] x = data; x.push(2); } @@ -435,7 +437,7 @@ This is a very interesting question. Suppose that we have a contract field set u mapping(string => string) comments; } - function somefunction { + function somefunction public { user user1; user1.comments["Hello"] = "World"; user user2 = user1; @@ -456,13 +458,13 @@ In this example:: pragma solidity ^0.4.0; contract B { - function B() payable {} + function B() public payable {} } contract A { address child; - function test() { + function test() public { child = (new B).value(10)(); //construct a new B with 10 wei } } @@ -501,17 +503,17 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) Sure. Take care that if you cross the memory / storage boundary, independent copies will be created:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { uint[20] x; - function f() { + function f() public { g(x); h(x); } - function g(uint[20] y) internal { + function g(uint[20] y) internal pure { y[2] = 3; } diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index aedc0c09f..6425dcaab 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -21,11 +21,11 @@ Storage contract SimpleStorage { uint storedData; - function set(uint x) { + function set(uint x) public { storedData = x; } - function get() constant returns (uint) { + function get() public constant returns (uint) { return storedData; } } @@ -94,16 +94,16 @@ registering with username and password - all you need is an Ethereum keypair. // This is the constructor whose code is // run only when the contract is created. - function Coin() { + function Coin() public { minter = msg.sender; } - function mint(address receiver, uint amount) { + function mint(address receiver, uint amount) public { if (msg.sender != minter) return; balances[receiver] += amount; } - function send(address receiver, uint amount) { + function send(address receiver, uint amount) public { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; @@ -145,7 +145,7 @@ like this one. The :ref:`getter function` created by the ``pub is a bit more complex in this case. It roughly looks like the following:: - function balances(address _account) returns (uint) { + function balances(address _account) public view returns (uint) { return balances[_account]; } diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 1e2138fa3..49fd7ea4e 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -62,7 +62,7 @@ complete contract): /// Mapping of ether shares of the contract. mapping(address => uint) shares; /// Withdraw your share. - function withdraw() { + function withdraw() public { if (msg.sender.send(shares[msg.sender])) shares[msg.sender] = 0; } @@ -85,7 +85,7 @@ as it uses ``call`` which forwards all remaining gas by default: /// Mapping of ether shares of the contract. mapping(address => uint) shares; /// Withdraw your share. - function withdraw() { + function withdraw() public { if (msg.sender.call.value(shares[msg.sender])()) shares[msg.sender] = 0; } @@ -102,7 +102,7 @@ outlined further below: /// Mapping of ether shares of the contract. mapping(address => uint) shares; /// Withdraw your share. - function withdraw() { + function withdraw() public { var share = shares[msg.sender]; shares[msg.sender] = 0; msg.sender.transfer(share); @@ -130,7 +130,7 @@ Sending and Receiving Ether - Neither contracts nor "external accounts" are currently able to prevent that someone sends them Ether. Contracts can react on and reject a regular transfer, but there are ways to move Ether without creating a message call. One way is to simply "mine to" - the contract address and the second way is using ``selfdestruct(x)``. + the contract address and the second way is using ``selfdestruct(x)``. - If a contract receives Ether (without a function being called), the fallback function is executed. If it does not have a fallback function, the Ether will be rejected (by throwing an exception). @@ -186,11 +186,11 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like contract TxUserWallet { address owner; - function TxUserWallet() { + function TxUserWallet() public { owner = msg.sender; } - function transferTo(address dest, uint amount) { + function transferTo(address dest, uint amount) public { require(tx.origin == owner); dest.transfer(amount); } @@ -203,17 +203,17 @@ Now someone tricks you into sending ether to the address of this attack wallet: pragma solidity ^0.4.11; interface TxUserWallet { - function transferTo(address dest, uint amount); + function transferTo(address dest, uint amount) public; } contract TxAttackWallet { address owner; - function TxAttackWallet() { + function TxAttackWallet() public { owner = msg.sender; } - function() { + function() public { TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); } } diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 9489665e9..6c01278aa 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -36,7 +36,7 @@ of votes. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.16; /// @title Voting with delegation. contract Ballot { @@ -66,7 +66,7 @@ of votes. Proposal[] public proposals; /// Create a new ballot to choose one of `proposalNames`. - function Ballot(bytes32[] proposalNames) { + function Ballot(bytes32[] proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; @@ -86,7 +86,7 @@ of votes. // Give `voter` the right to vote on this ballot. // May only be called by `chairperson`. - function giveRightToVote(address voter) { + function giveRightToVote(address voter) public { // If the argument of `require` evaluates to `false`, // it terminates and reverts all changes to // the state and to Ether balances. It is often @@ -99,7 +99,7 @@ of votes. } /// Delegate your vote to the voter `to`. - function delegate(address to) { + function delegate(address to) public { // assigns reference Voter storage sender = voters[msg.sender]; require(!sender.voted); @@ -140,7 +140,7 @@ of votes. /// Give your vote (including votes delegated to you) /// to proposal `proposals[proposal].name`. - function vote(uint proposal) { + function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; require(!sender.voted); sender.voted = true; @@ -154,13 +154,13 @@ of votes. /// @dev Computes the winning proposal taking all /// previous votes into account. - function winningProposal() constant + function winningProposal() public view returns (uint winningProposal) { uint winningVoteCount = 0; - for (uint p = 0; p < proposals.length; p++) { - if (proposals[p].voteCount > winningVoteCount) { - winningVoteCount = proposals[p].voteCount; + for (uint p = 0; p < winningProposals.length; p++) { + if (winningProposals[p].voteCount > winningVoteCount) { + winningVoteCount = winningProposals[p].voteCount; winningProposal = p; } } @@ -169,10 +169,10 @@ of votes. // Calls winningProposal() function to get the index // of the winner contained in the proposals array and then // returns the name of the winner - function winnerName() constant + function winnerName() public view returns (bytes32 winnerName) { - winnerName = proposals[winningProposal()].name; + name = proposals[winningProposal()].name; } } @@ -248,7 +248,7 @@ activate themselves. function SimpleAuction( uint _biddingTime, address _beneficiary - ) { + ) public { beneficiary = _beneficiary; auctionEnd = now + _biddingTime; } @@ -257,7 +257,7 @@ activate themselves. /// together with this transaction. /// The value will only be refunded if the /// auction is not won. - function bid() payable { + function bid() public payable { // No arguments are necessary, all // information is already part of // the transaction. The keyword payable @@ -286,7 +286,7 @@ activate themselves. } /// Withdraw a bid that was overbid. - function withdraw() returns (bool) { + function withdraw() public returns (bool) { uint amount = pendingReturns[msg.sender]; if (amount > 0) { // It is important to set this to zero because the recipient @@ -305,7 +305,7 @@ activate themselves. /// End the auction and send the highest bid /// to the beneficiary. - function auctionEnd() { + function auctionEnd() public { // It is a good guideline to structure functions that interact // with other contracts (i.e. they call functions or send Ether) // into three phases: @@ -405,7 +405,7 @@ high or low invalid bids. uint _biddingTime, uint _revealTime, address _beneficiary - ) { + ) public { beneficiary = _beneficiary; biddingEnd = now + _biddingTime; revealEnd = biddingEnd + _revealTime; @@ -421,6 +421,7 @@ high or low invalid bids. /// still make the required deposit. The same address can /// place multiple bids. function bid(bytes32 _blindedBid) + public payable onlyBefore(biddingEnd) { @@ -438,6 +439,7 @@ high or low invalid bids. bool[] _fake, bytes32[] _secret ) + public onlyAfter(biddingEnd) onlyBefore(revealEnd) { @@ -487,7 +489,7 @@ high or low invalid bids. } /// Withdraw a bid that was overbid. - function withdraw() { + function withdraw() public { uint amount = pendingReturns[msg.sender]; if (amount > 0) { // It is important to set this to zero because the recipient @@ -503,6 +505,7 @@ high or low invalid bids. /// End the auction and send the highest bid /// to the beneficiary. function auctionEnd() + public onlyAfter(revealEnd) { require(!ended); @@ -533,7 +536,7 @@ Safe Remote Purchase // Ensure that `msg.value` is an even number. // Division will truncate if it is an odd number. // Check via multiplication that it wasn't an odd number. - function Purchase() payable { + function Purchase() public payable { seller = msg.sender; value = msg.value / 2; require((2 * value) == msg.value); @@ -567,6 +570,7 @@ Safe Remote Purchase /// Can only be called by the seller before /// the contract is locked. function abort() + public onlySeller inState(State.Created) { @@ -580,6 +584,7 @@ Safe Remote Purchase /// The ether will be locked until confirmReceived /// is called. function confirmPurchase() + public inState(State.Created) condition(msg.value == (2 * value)) payable @@ -592,6 +597,7 @@ Safe Remote Purchase /// Confirm that you (the buyer) received the item. /// This will release the locked ether. function confirmReceived() + public onlyBuyer inState(State.Locked) { diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 0b5548004..e475fcd19 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -43,7 +43,7 @@ Functions are the executable units of code within a contract. pragma solidity ^0.4.0; contract SimpleAuction { - function bid() payable { // Function + function bid() public payable { // Function // ... } } @@ -72,7 +72,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat _; } - function abort() onlySeller { // Modifier usage + function abort() public onlySeller { // Modifier usage // ... } } @@ -91,7 +91,7 @@ Events are convenience interfaces with the EVM logging facilities. contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event - function bid() payable { + function bid() public payable { // ... HighestBidIncreased(msg.sender, msg.value); // Triggering event } diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 5b6f42a2e..66fecff81 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -86,17 +86,17 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu Yes:: contract A { - function spam(); - function ham(); + function spam() public; + function ham() public; } contract B is A { - function spam() { + function spam() public { ... } - function ham() { + function ham() public { ... } } @@ -104,10 +104,10 @@ Yes:: No:: contract A { - function spam() { + function spam() public { ... } - function ham() { + function ham() public { ... } } @@ -169,26 +169,26 @@ Within a grouping, place the ``constant`` functions last. Yes:: contract A { - function A() { + function A() public { ... } - - function() { + + function() public { ... } - + // External functions // ... - + // External functions that are constant // ... - + // Public functions // ... - + // Internal functions // ... - + // Private functions // ... } @@ -196,7 +196,7 @@ Yes:: No:: contract A { - + // External functions // ... @@ -206,16 +206,16 @@ No:: // Public functions // ... - function A() { + function A() public { ... } - - function() { + + function() public { ... } // Internal functions - // ... + // ... } Whitespace in Expressions @@ -235,17 +235,17 @@ No:: Exception:: - function singleLine() { spam(); } + function singleLine() public { spam(); } Immediately before a comma, semicolon: Yes:: - function spam(uint i, Coin coin); + function spam(uint i, Coin coin) public; No:: - function spam(uint i , Coin coin) ; + function spam(uint i , Coin coin) public ; More than one space around an assignment or other operator to align with another: @@ -266,13 +266,13 @@ Don't include a whitespace in the fallback function: Yes:: - function() { + function() public { ... } No:: - - function () { + + function () public { ... } @@ -395,30 +395,30 @@ The opening brace should be preceeded by a single space. Yes:: - function increment(uint x) returns (uint) { + function increment(uint x) public pure returns (uint) { return x + 1; } - function increment(uint x) public onlyowner returns (uint) { + function increment(uint x) public pure onlyowner returns (uint) { return x + 1; } No:: - function increment(uint x) returns (uint) + function increment(uint x) public pure returns (uint) { return x + 1; } - function increment(uint x) returns (uint){ + function increment(uint x) public pure returns (uint){ return x + 1; } - function increment(uint x) returns (uint) { + function increment(uint x) public pure returns (uint) { return x + 1; } - function increment(uint x) returns (uint) { + function increment(uint x) public pure returns (uint) { return x + 1;} The visibility modifiers for a function should come before any custom @@ -450,14 +450,16 @@ Yes:: address d, address e, address f - ) { + ) + public + { doSomething(); } No:: function thisFunctionHasLotsOfArguments(address a, address b, address c, - address d, address e, address f) { + address d, address e, address f) public { doSomething(); } @@ -466,7 +468,7 @@ No:: address c, address d, address e, - address f) { + address f) public { doSomething(); } @@ -476,12 +478,12 @@ No:: address c, address d, address e, - address f) { + address f) public { doSomething(); } If a long function declaration has modifiers, then each modifier should be -dropped to it's own line. +dropped to its own line. Yes:: @@ -542,6 +544,7 @@ Yes:: B(param1) C(param2, param3) D(param4) + public { // do something with param5 } @@ -554,6 +557,7 @@ No:: B(param1) C(param2, param3) D(param4) + public { // do something with param5 } @@ -563,7 +567,8 @@ No:: function A(uint param1, uint param2, uint param3, uint param4, uint param5) B(param1) C(param2, param3) - D(param4) { + D(param4) + public { // do something with param5 } } @@ -572,7 +577,7 @@ When declaring short functions with a single statement, it is permissible to do Permissible:: - function shortFunction() { doSomething(); } + function shortFunction() public { doSomething(); } These guidelines for function declarations are intended to improve readability. Authors should use their best judgement as this guide does not try to cover all diff --git a/docs/types.rst b/docs/types.rst index c716b95ea..fbebb1aad 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -79,8 +79,8 @@ Fixed Point Numbers Fixed point numbers are not fully supported by Solidity yet. They can be declared, but cannot be assigned to or from. -``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represent the number of bits taken by -the type and ``N`` represent how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. +``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by +the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. ``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively. Operators: @@ -331,14 +331,14 @@ check the value ranges at runtime and a failure causes an exception. Enums need :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } ActionChoices choice; ActionChoices constant defaultChoice = ActionChoices.GoStraight; - function setGoStraight() { + function setGoStraight() public { choice = ActionChoices.GoStraight; } @@ -347,11 +347,11 @@ check the value ranges at runtime and a failure causes an exception. Enums need // for all matters external to Solidity. The integer type used is just // large enough to hold all enum values, i.e. if you have more values, // `uint16` will be used and so on. - function getChoice() returns (ActionChoices) { + function getChoice() public view returns (ActionChoices) { return choice; } - function getDefaultChoice() returns (uint) { + function getDefaultChoice() public pure returns (uint) { return uint(defaultChoice); } } @@ -409,23 +409,24 @@ just use ``f``, if you want to use its external form, use ``this.f``. Additionally, public (or external) functions also have a special member called ``selector``, which returns the :ref:`ABI function selector `:: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract Selector { - function f() returns (bytes4) { + function f() public view returns (bytes4) { return this.f.selector; } } Example that shows how to use internal function types:: - pragma solidity ^0.4.5; + pragma solidity ^0.4.16; library ArrayUtils { // internal functions can be used in internal library functions because // they will be part of the same code context - function map(uint[] memory self, function (uint) returns (uint) f) + function map(uint[] memory self, function (uint) pure returns (uint) f) internal + pure returns (uint[] memory r) { r = new uint[](self.length); @@ -435,9 +436,10 @@ Example that shows how to use internal function types:: } function reduce( uint[] memory self, - function (uint, uint) returns (uint) f + function (uint, uint) pure returns (uint) f ) internal + pure returns (uint r) { r = self[0]; @@ -445,23 +447,23 @@ Example that shows how to use internal function types:: r = f(r, self[i]); } } - function range(uint length) internal returns (uint[] memory r) { + function range(uint length) internal pure returns (uint[] memory r) { r = new uint[](length); for (uint i = 0; i < r.length; i++) { r[i] = i; } } } - + contract Pyramid { using ArrayUtils for *; - function pyramid(uint l) returns (uint) { + function pyramid(uint l) public pure returns (uint) { return ArrayUtils.range(l).map(square).reduce(sum); } - function square(uint x) internal returns (uint) { + function square(uint x) internal pure returns (uint) { return x * x; } - function sum(uint x, uint y) internal returns (uint) { + function sum(uint x, uint y) internal pure returns (uint) { return x + y; } } @@ -477,11 +479,11 @@ Another example that uses external function types:: } Request[] requests; event NewRequest(uint); - function query(bytes data, function(bytes memory) external callback) { + function query(bytes data, function(bytes memory) external callback) public { requests.push(Request(data, callback)); NewRequest(requests.length - 1); } - function reply(uint requestID, bytes response) { + function reply(uint requestID, bytes response) public { // Here goes the check that the reply comes from a trusted source requests[requestID].callback(response); } @@ -492,7 +494,7 @@ Another example that uses external function types:: function buySomething() { oracle.query("USD", this.oracleResponse); } - function oracleResponse(bytes response) { + function oracleResponse(bytes response) public { require(msg.sender == address(oracle)); // Use the data } @@ -543,7 +545,7 @@ memory-stored reference type do not create a copy. uint[] x; // the data location of x is storage // the data location of memoryArray is memory - function f(uint[] memoryArray) { + function f(uint[] memoryArray) public { x = memoryArray; // works, copies the whole array to storage var y = x; // works, assigns a pointer, data location of y is storage y[7]; // fine, returns the 8th element @@ -560,7 +562,7 @@ memory-stored reference type do not create a copy. } function g(uint[] storage storageArray) internal {} - function h(uint[] memoryArray) {} + function h(uint[] memoryArray) public {} } Summary @@ -620,10 +622,10 @@ the ``.length`` member. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { - function f(uint len) { + function f(uint len) public pure { uint[] memory a = new uint[](7); bytes memory b = new bytes(len); // Here we have a.length == 7 and b.length == len @@ -641,13 +643,13 @@ assigned to a variable right away. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract C { - function f() { + function f() public pure { g([uint(1), 2, 3]); } - function g(uint[3] _data) { + function g(uint[3] _data) public { // ... } } @@ -667,7 +669,7 @@ possible: pragma solidity ^0.4.0; contract C { - function f() { + function f() public { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] x = [uint(1), 3, 4]; @@ -703,7 +705,7 @@ Members :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.16; contract ArrayContract { uint[2**20] m_aLotOfIntegers; @@ -712,23 +714,23 @@ Members bool[2][] m_pairsOfFlags; // newPairs is stored in memory - the default for function arguments - function setAllFlagPairs(bool[2][] newPairs) { + function setAllFlagPairs(bool[2][] newPairs) public { // assignment to a storage array replaces the complete array m_pairsOfFlags = newPairs; } - function setFlagPair(uint index, bool flagA, bool flagB) { + function setFlagPair(uint index, bool flagA, bool flagB) public { // access to a non-existing index will throw an exception m_pairsOfFlags[index][0] = flagA; m_pairsOfFlags[index][1] = flagB; } - function changeFlagArraySize(uint newSize) { + function changeFlagArraySize(uint newSize) public { // if the new size is smaller, removed array elements will be cleared m_pairsOfFlags.length = newSize; } - function clear() { + function clear() public { // these clear the arrays completely delete m_pairsOfFlags; delete m_aLotOfIntegers; @@ -738,20 +740,20 @@ Members bytes m_byteData; - function byteArrays(bytes data) { + function byteArrays(bytes data) public { // byte arrays ("bytes") are different as they are stored without padding, // but can be treated identical to "uint8[]" m_byteData = data; m_byteData.length += 7; - m_byteData[3] = 8; + m_byteData[3] = byte(8); delete m_byteData[2]; } - function addFlag(bool[2] flag) returns (uint) { + function addFlag(bool[2] flag) public returns (uint) { return m_pairsOfFlags.push(flag); } - function createMemoryArray(uint size) returns (bytes) { + function createMemoryArray(uint size) public pure returns (bytes) { // Dynamic memory arrays are created using `new`: uint[2][] memory arrayOfPairs = new uint[2][](size); // Create a dynamic byte array: @@ -795,13 +797,13 @@ shown in the following example: uint numCampaigns; mapping (uint => Campaign) campaigns; - function newCampaign(address beneficiary, uint goal) returns (uint campaignID) { + function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable // Creates new struct and saves in storage. We leave out the mapping type. campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); } - function contribute(uint campaignID) payable { + function contribute(uint campaignID) public payable { Campaign storage c = campaigns[campaignID]; // Creates a new temporary memory struct, initialised with the given values // and copies it over to storage. @@ -810,7 +812,7 @@ shown in the following example: c.amount += msg.value; } - function checkGoalReached(uint campaignID) returns (bool reached) { + function checkGoalReached(uint campaignID) public returns (bool reached) { Campaign storage c = campaigns[campaignID]; if (c.amount < c.fundingGoal) return false; @@ -872,13 +874,13 @@ for each ``_KeyType``, recursively. contract MappingExample { mapping(address => uint) public balances; - function update(uint newBalance) { + function update(uint newBalance) public { balances[msg.sender] = newBalance; } } contract MappingUser { - function f() returns (uint) { + function f() public returns (uint) { MappingExample m = new MappingExample(); m.update(100); return m.balances(this); @@ -916,11 +918,11 @@ It is important to note that ``delete a`` really behaves like an assignment to ` uint data; uint[] dataArray; - function f() { + function f() public { uint x = data; delete x; // sets x to 0, does not affect data delete data; // sets data to 0, does not affect x which still holds a copy - uint[] y = dataArray; + uint[] storage y = dataArray; delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also // y is affected which is an alias to the storage object // On the other hand: "delete y" is not valid, as assignments to local variables diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 8261bdde7..ce58cf56f 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -34,7 +34,7 @@ library has to be updated by an external oracle. These suffixes cannot be applied to variables. If you want to interpret some input variable in e.g. days, you can do it in the following way:: - function f(uint start, uint daysAfter) { + function f(uint start, uint daysAfter) public { if (now >= start + daysAfter * 1 days) { // ... } From 93cf4dee666e01d6907c75c4018a701e5069daad Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Wed, 13 Dec 2017 07:55:46 +0000 Subject: [PATCH 048/186] Fixes for failing tests --- docs/assembly.rst | 4 ++-- docs/control-structures.rst | 2 +- docs/solidity-by-example.rst | 8 ++++---- docs/types.rst | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index e09a556b3..825892b37 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -91,7 +91,7 @@ you really know what you are doing. // We know that we only access the array in bounds, so we can avoid the check. // 0x20 needs to be added to an array because the first slot contains the // array length. - function sumAsm(uint[] _data) returns (uint o_sum) public { + function sumAsm(uint[] _data) public returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) { assembly { o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20)))) @@ -100,7 +100,7 @@ you really know what you are doing. } // Same as above, but accomplish the entire code within inline assembly. - function sumPureAsm(uint[] _data) returns (uint o_sum) public { + function sumPureAsm(uint[] _data) public returns (uint o_sum) { assembly { // Load the length (first 32 bytes) let len := mload(_data) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 079b14c75..0c5825bc5 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -135,7 +135,7 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe contract Consumer { InfoFeed feed; function setFeed(address addr) public { feed = InfoFeed(addr); } - function public callFeed() { feed.info.value(10).gas(800)(); } + function callFeed() public { feed.info.value(10).gas(800)(); } } The modifier ``payable`` has to be used for ``info``, because otherwise, the `.value()` diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 6c01278aa..b663083cd 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -158,9 +158,9 @@ of votes. returns (uint winningProposal) { uint winningVoteCount = 0; - for (uint p = 0; p < winningProposals.length; p++) { - if (winningProposals[p].voteCount > winningVoteCount) { - winningVoteCount = winningProposals[p].voteCount; + for (uint p = 0; p < proposals.length; p++) { + if (proposals[p].voteCount > winningVoteCount) { + winningVoteCount = proposals[p].voteCount; winningProposal = p; } } @@ -172,7 +172,7 @@ of votes. function winnerName() public view returns (bytes32 winnerName) { - name = proposals[winningProposal()].name; + winnerName = proposals[winningProposal()].name; } } diff --git a/docs/types.rst b/docs/types.rst index fbebb1aad..98009b3fb 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -649,7 +649,7 @@ assigned to a variable right away. function f() public pure { g([uint(1), 2, 3]); } - function g(uint[3] _data) public { + function g(uint[3] _data) public pure { // ... } } From 54b6739962ef45319777ce2aebafdf4b91412d84 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 8 Dec 2017 14:01:22 +0100 Subject: [PATCH 049/186] Separate expression and statement. --- libjulia/ASTDataForward.h | 4 +- libjulia/backends/evm/EVMCodeTransform.cpp | 9 ++- libjulia/backends/evm/EVMCodeTransform.h | 3 +- libjulia/optimiser/ASTCopier.cpp | 20 +++-- libjulia/optimiser/ASTCopier.h | 57 ++++++++++---- libjulia/optimiser/ASTWalker.cpp | 10 +++ libjulia/optimiser/ASTWalker.h | 6 ++ libjulia/optimiser/Substitution.cpp | 8 +- libjulia/optimiser/Substitution.h | 7 +- libsolidity/analysis/ViewPureChecker.cpp | 4 + libsolidity/inlineasm/AsmAnalysis.cpp | 16 +++- libsolidity/inlineasm/AsmAnalysis.h | 3 +- libsolidity/inlineasm/AsmData.h | 16 ++-- libsolidity/inlineasm/AsmDataForward.h | 4 +- libsolidity/inlineasm/AsmParser.cpp | 91 +++++++++++++--------- libsolidity/inlineasm/AsmParser.h | 8 +- libsolidity/inlineasm/AsmPrinter.cpp | 5 ++ libsolidity/inlineasm/AsmPrinter.h | 1 + libsolidity/inlineasm/AsmScopeFiller.cpp | 5 ++ libsolidity/inlineasm/AsmScopeFiller.h | 1 + 20 files changed, 197 insertions(+), 81 deletions(-) diff --git a/libjulia/ASTDataForward.h b/libjulia/ASTDataForward.h index 3806e321d..143b9c46a 100644 --- a/libjulia/ASTDataForward.h +++ b/libjulia/ASTDataForward.h @@ -42,11 +42,13 @@ using If = solidity::assembly::If; using Case = solidity::assembly::Case; using Switch = solidity::assembly::Switch; using ForLoop = solidity::assembly::ForLoop; +using ExpressionStatement = solidity::assembly::ExpressionStatement; using Block = solidity::assembly::Block; using TypedName = solidity::assembly::TypedName; -using Statement = boost::variant; +using Expression = boost::variant; +using Statement = boost::variant; } } diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index f92939be9..0c7365fba 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -77,6 +77,13 @@ void CodeTransform::operator()(StackAssignment const& _assignment) checkStackHeight(&_assignment); } +void CodeTransform::operator()(ExpressionStatement const& _statement) +{ + m_assembly.setSourceLocation(_statement.location); + boost::apply_visitor(*this, _statement.expression); + checkStackHeight(&_statement); +} + void CodeTransform::operator()(Label const& _label) { m_assembly.setSourceLocation(_label.location); @@ -460,7 +467,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Sc return m_context->functionEntryIDs[&_function]; } -void CodeTransform::visitExpression(Statement const& _expression) +void CodeTransform::visitExpression(Expression const& _expression) { int height = m_assembly.stackHeight(); boost::apply_visitor(*this, _expression); diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 577cc8bab..0f2aaf955 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -101,6 +101,7 @@ public: void operator()(Identifier const& _identifier); void operator()(FunctionalInstruction const& _instr); void operator()(FunctionCall const&); + void operator()(ExpressionStatement const& _statement); void operator()(Label const& _label); void operator()(StackAssignment const& _assignment); void operator()(Assignment const& _assignment); @@ -118,7 +119,7 @@ private: AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. - void visitExpression(Statement const& _expression); + void visitExpression(Expression const& _expression); void visitStatements(std::vector const& _statements); diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp index a461f434b..5c47be64b 100644 --- a/libjulia/optimiser/ASTCopier.cpp +++ b/libjulia/optimiser/ASTCopier.cpp @@ -37,6 +37,11 @@ Statement ASTCopier::operator()(Instruction const&) return {}; } +Statement ASTCopier::operator()(ExpressionStatement const& _statement) +{ + return ExpressionStatement{ _statement.location, translate(_statement.expression) }; +} + Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) { return VariableDeclaration{ @@ -67,7 +72,7 @@ Statement ASTCopier::operator()(Label const&) return {}; } -Statement ASTCopier::operator()(FunctionCall const& _call) +Expression ASTCopier::operator()(FunctionCall const& _call) { return FunctionCall{ _call.location, @@ -76,7 +81,7 @@ Statement ASTCopier::operator()(FunctionCall const& _call) }; } -Statement ASTCopier::operator()(FunctionalInstruction const& _instruction) +Expression ASTCopier::operator()(FunctionalInstruction const& _instruction) { return FunctionalInstruction{ _instruction.location, @@ -85,12 +90,12 @@ Statement ASTCopier::operator()(FunctionalInstruction const& _instruction) }; } -Statement ASTCopier::operator()(Identifier const& _identifier) +Expression ASTCopier::operator()(Identifier const& _identifier) { return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; } -Statement ASTCopier::operator()(Literal const& _literal) +Expression ASTCopier::operator()(Literal const& _literal) { return translate(_literal); } @@ -140,9 +145,14 @@ Statement ASTCopier::operator ()(Block const& _block) return translate(_block); } +Expression ASTCopier::translate(Expression const& _expression) +{ + return _expression.apply_visitor(static_cast(*this)); +} + Statement ASTCopier::translate(Statement const& _statement) { - return boost::apply_visitor(*this, _statement); + return _statement.apply_visitor(static_cast(*this)); } Block ASTCopier::translate(Block const& _block) diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h index 5dde2ce9d..36a1ced51 100644 --- a/libjulia/optimiser/ASTCopier.h +++ b/libjulia/optimiser/ASTCopier.h @@ -34,28 +34,55 @@ namespace dev namespace julia { +class ExpressionCopier: public boost::static_visitor +{ +public: + virtual Expression operator()(Literal const& _literal) = 0; + virtual Expression operator()(Identifier const& _identifier) = 0; + virtual Expression operator()(FunctionalInstruction const& _instr) = 0; + virtual Expression operator()(FunctionCall const&) = 0; +}; + +class StatementCopier: public boost::static_visitor +{ +public: + virtual Statement operator()(ExpressionStatement const& _statement) = 0; + virtual Statement operator()(Instruction const& _instruction) = 0; + virtual Statement operator()(Label const& _label) = 0; + virtual Statement operator()(StackAssignment const& _assignment) = 0; + virtual Statement operator()(Assignment const& _assignment) = 0; + virtual Statement operator()(VariableDeclaration const& _varDecl) = 0; + virtual Statement operator()(If const& _if) = 0; + virtual Statement operator()(Switch const& _switch) = 0; + virtual Statement operator()(FunctionDefinition const&) = 0; + virtual Statement operator()(ForLoop const&) = 0; + virtual Statement operator()(Block const& _block) = 0; +}; + /** * Creates a copy of a iulia AST potentially replacing identifier names. * Base class to be extended. */ -class ASTCopier: public boost::static_visitor +class ASTCopier: public ExpressionCopier, public StatementCopier { public: - Statement operator()(Literal const& _literal); - Statement operator()(Instruction const& _instruction); - Statement operator()(Identifier const& _identifier); - Statement operator()(FunctionalInstruction const& _instr); - Statement operator()(FunctionCall const&); - Statement operator()(Label const& _label); - Statement operator()(StackAssignment const& _assignment); - Statement operator()(Assignment const& _assignment); - Statement operator()(VariableDeclaration const& _varDecl); - Statement operator()(If const& _if); - Statement operator()(Switch const& _switch); - Statement operator()(FunctionDefinition const&); - Statement operator()(ForLoop const&); - Statement operator()(Block const& _block); + virtual Expression operator()(Literal const& _literal) override; + virtual Statement operator()(Instruction const& _instruction) override; + virtual Expression operator()(Identifier const& _identifier) override; + virtual Expression operator()(FunctionalInstruction const& _instr) override; + virtual Expression operator()(FunctionCall const&) override; + virtual Statement operator()(ExpressionStatement const& _statement) override; + virtual Statement operator()(Label const& _label) override; + virtual Statement operator()(StackAssignment const& _assignment) override; + virtual Statement operator()(Assignment const& _assignment) override; + virtual Statement operator()(VariableDeclaration const& _varDecl) override; + virtual Statement operator()(If const& _if) override; + virtual Statement operator()(Switch const& _switch) override; + virtual Statement operator()(FunctionDefinition const&) override; + virtual Statement operator()(ForLoop const&) override; + virtual Statement operator()(Block const& _block) override; + virtual Expression translate(Expression const& _expression); virtual Statement translate(Statement const& _statement); protected: diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp index 0caef04e0..499b4bf20 100644 --- a/libjulia/optimiser/ASTWalker.cpp +++ b/libjulia/optimiser/ASTWalker.cpp @@ -42,6 +42,11 @@ void ASTWalker::operator()(FunctionCall const& _funCall) walkVector(_funCall.arguments | boost::adaptors::reversed); } +void ASTWalker::operator()(ExpressionStatement const& _statement) +{ + boost::apply_visitor(*this, _statement.expression); +} + void ASTWalker::operator()(Assignment const& _assignment) { for (auto const& name: _assignment.variableNames) @@ -100,6 +105,11 @@ void ASTModifier::operator()(FunctionCall& _funCall) walkVector(_funCall.arguments | boost::adaptors::reversed); } +void ASTModifier::operator()(ExpressionStatement& _statement) +{ + boost::apply_visitor(*this, _statement.expression); +} + void ASTModifier::operator()(Assignment& _assignment) { for (auto& name: _assignment.variableNames) diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h index 8bd867d5a..4652a353c 100644 --- a/libjulia/optimiser/ASTWalker.h +++ b/libjulia/optimiser/ASTWalker.h @@ -47,6 +47,7 @@ public: virtual void operator()(Identifier const&) {} virtual void operator()(FunctionalInstruction const& _instr); virtual void operator()(FunctionCall const& _funCall); + virtual void operator()(ExpressionStatement const& _statement); virtual void operator()(Label const&) { solAssert(false, ""); } virtual void operator()(StackAssignment const&) { solAssert(false, ""); } virtual void operator()(Assignment const& _assignment); @@ -77,6 +78,7 @@ public: virtual void operator()(Identifier&) {} virtual void operator()(FunctionalInstruction& _instr); virtual void operator()(FunctionCall& _funCall); + virtual void operator()(ExpressionStatement& _statement); virtual void operator()(Label&) { solAssert(false, ""); } virtual void operator()(StackAssignment&) { solAssert(false, ""); } virtual void operator()(Assignment& _assignment); @@ -98,6 +100,10 @@ protected: { boost::apply_visitor(*this, _st); } + virtual void visit(Expression& _e) + { + boost::apply_visitor(*this, _e); + } }; } diff --git a/libjulia/optimiser/Substitution.cpp b/libjulia/optimiser/Substitution.cpp index a49f1f7a7..668b6cb64 100644 --- a/libjulia/optimiser/Substitution.cpp +++ b/libjulia/optimiser/Substitution.cpp @@ -26,14 +26,14 @@ using namespace std; using namespace dev; using namespace dev::julia; -Statement Substitution::translate(Statement const& _statement) +Expression Substitution::translate(Expression const& _expression) { - if (_statement.type() == typeid(Identifier)) + if (_expression.type() == typeid(Identifier)) { - string const& name = boost::get(_statement).name; + string const& name = boost::get(_expression).name; if (m_substitutions.count(name)) // No recursive substitution return ASTCopier().translate(*m_substitutions.at(name)); } - return ASTCopier::translate(_statement); + return ASTCopier::translate(_expression); } diff --git a/libjulia/optimiser/Substitution.h b/libjulia/optimiser/Substitution.h index 10bdf32e6..313a08d78 100644 --- a/libjulia/optimiser/Substitution.h +++ b/libjulia/optimiser/Substitution.h @@ -33,18 +33,17 @@ namespace julia /** * Specific AST copier that replaces certain identifiers with expressions. - * Only works on ASTs that are expressions. */ class Substitution: public ASTCopier { public: - Substitution(std::map const& _substitutions): + Substitution(std::map const& _substitutions): m_substitutions(_substitutions) {} - virtual Statement translate(Statement const& _statement) override; + virtual Expression translate(Expression const& _expression) override; private: - std::map const& m_substitutions; + std::map const& m_substitutions; }; } diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 6788cb059..6257ac6d8 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -50,6 +50,10 @@ public: for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } + void operator()(assembly::ExpressionStatement const& _expr) + { + boost::apply_visitor(*this, _expr.expression); + } void operator()(assembly::StackAssignment const&) {} void operator()(assembly::Assignment const& _assignment) { diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 049af65fb..17b7cce09 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -155,6 +155,16 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) return success; } +bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) +{ +// size_t initialStackHeight = m_stackHeight; + bool success = boost::apply_visitor(*this, _statement.expression); +// if (!expectDeposit(0, initialStackHeight, _statement.location)) +// success = false; + m_info.stackHeightInfo[&_statement] = m_stackHeight; + return success; +} + bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { solAssert(!m_julia, ""); @@ -407,13 +417,13 @@ bool AsmAnalyzer::operator()(Block const& _block) return success; } -bool AsmAnalyzer::expectExpression(Statement const& _statement) +bool AsmAnalyzer::expectExpression(Expression const& _expr) { bool success = true; int const initialHeight = m_stackHeight; - if (!boost::apply_visitor(*this, _statement)) + if (!boost::apply_visitor(*this, _expr)) success = false; - if (!expectDeposit(1, initialHeight, locationOf(_statement))) + if (!expectDeposit(1, initialHeight, locationOf(_expr))) success = false; return success; } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index e484b876d..00a33db3e 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -65,6 +65,7 @@ public: bool operator()(assembly::Identifier const&); bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); bool operator()(assembly::Label const& _label); + bool operator()(assembly::ExpressionStatement const&); bool operator()(assembly::StackAssignment const&); bool operator()(assembly::Assignment const& _assignment); bool operator()(assembly::VariableDeclaration const& _variableDeclaration); @@ -77,7 +78,7 @@ public: private: /// Visits the statement and expects it to deposit one item onto the stack. - bool expectExpression(Statement const& _statement); + bool expectExpression(Expression const& _expr); bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); /// Verifies that a variable to be assigned to exists and has the same size diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 11e56fae1..2982d5e02 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; +struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; -struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; +struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; +struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; +/// Statement that contains only a single expression +struct ExpressionStatement { SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; +struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { SourceLocation location; std::shared_ptr condition; Block body; }; +struct If { SourceLocation location; std::shared_ptr condition; Block body; }; /// Switch case or default case struct Case { SourceLocation location; std::shared_ptr value; Block body; }; /// Switch statement -struct Switch { SourceLocation location; std::shared_ptr expression; std::vector cases; }; -struct ForLoop { SourceLocation location; Block pre; std::shared_ptr condition; Block post; Block body; }; +struct Switch { SourceLocation location; std::shared_ptr expression; std::vector cases; }; +struct ForLoop { SourceLocation location; Block pre; std::shared_ptr condition; Block post; Block body; }; struct LocationExtractor: boost::static_visitor { diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index 1ab62cc08..317e257c9 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -45,11 +45,13 @@ struct If; struct Switch; struct Case; struct ForLoop; +struct ExpressionStatement; struct Block; struct TypedName; -using Statement = boost::variant; +using Expression = boost::variant; +using Statement = boost::variant; } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 4f8802a0b..273e1d5c0 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement() { assembly::If _if = createWithLocation(); m_scanner->next(); - _if.condition = make_shared(parseExpression()); - if (_if.condition->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as conditions for if - try to append \"()\"."); + _if.condition = make_shared(parseExpression()); _if.body = parseBlock(); return _if; } @@ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement() { assembly::Switch _switch = createWithLocation(); m_scanner->next(); - _switch.expression = make_shared(parseExpression()); - if (_switch.expression->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as expressions for switch - try to append \"()\"."); + _switch.expression = make_shared(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) @@ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - Statement statement(parseElementaryOperation(false)); + ElementaryOperation elementary(parseElementaryOperation(false)); switch (currentToken()) { case Token::LParen: - return parseCall(std::move(statement)); + { + Expression expr = parseCall(std::move(elementary)); + return ExpressionStatement{locationOf(expr), expr}; + } case Token::Comma: { // if a comma follows, a multiple assignment is assumed - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); - assembly::Identifier const& identifier = boost::get(statement); + assembly::Identifier const& identifier = boost::get(elementary); Assignment assignment = createWithLocation(identifier.location); assignment.variableNames.emplace_back(identifier); @@ -146,25 +145,25 @@ assembly::Statement Parser::parseStatement() do { expectToken(Token::Comma); - statement = parseElementaryOperation(false); - if (statement.type() != typeid(assembly::Identifier)) + elementary = parseElementaryOperation(false); + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignemnt."); - assignment.variableNames.emplace_back(boost::get(statement)); + assignment.variableNames.emplace_back(boost::get(elementary)); } while (currentToken() == Token::Comma); expectToken(Token::Colon); expectToken(Token::Assign); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } case Token::Colon: { - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); - assembly::Identifier const& identifier = boost::get(statement); + assembly::Identifier const& identifier = boost::get(elementary); advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). @@ -175,7 +174,7 @@ assembly::Statement Parser::parseStatement() fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } @@ -194,7 +193,21 @@ assembly::Statement Parser::parseStatement() fatalParserError("Call or assignment expected."); break; } - return statement; + if (elementary.type() == typeid(assembly::Identifier)) + { + Expression expr = boost::get(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else if (elementary.type() == typeid(assembly::Literal)) + { + Expression expr = boost::get(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else + { + solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation."); + return boost::get(elementary); + } } assembly::Case Parser::parseCase() @@ -206,10 +219,10 @@ assembly::Case Parser::parseCase() else if (m_scanner->currentToken() == Token::Case) { m_scanner->next(); - assembly::Statement statement = parseElementaryOperation(); - if (statement.type() != typeid(assembly::Literal)) + ElementaryOperation literal = parseElementaryOperation(); + if (literal.type() != typeid(assembly::Literal)) fatalParserError("Literal expected."); - _case.value = make_shared(std::move(boost::get(statement))); + _case.value = make_shared(boost::get(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -224,19 +237,17 @@ assembly::ForLoop Parser::parseForLoop() ForLoop forLoop = createWithLocation(); expectToken(Token::For); forLoop.pre = parseBlock(); - forLoop.condition = make_shared(parseExpression()); - if (forLoop.condition->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as conditions for the for statement."); + forLoop.condition = make_shared(parseExpression()); forLoop.post = parseBlock(); forLoop.body = parseBlock(); forLoop.location.end = forLoop.body.location.end; return forLoop; } -assembly::Statement Parser::parseExpression() +assembly::Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - Statement operation = parseElementaryOperation(true); + ElementaryOperation operation = parseElementaryOperation(true); if (operation.type() == typeid(Instruction)) { Instruction const& instr = boost::get(operation); @@ -252,8 +263,18 @@ assembly::Statement Parser::parseExpression() } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); + else if (operation.type() == typeid(Instruction)) + { + Instruction& instr = boost::get(operation); + return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; + } + else if (operation.type() == typeid(assembly::Identifier)) + return boost::get(operation); else - return operation; + { + solAssert(operation.type() == typeid(assembly::Literal), ""); + return boost::get(operation); + } } std::map const& Parser::instructions() @@ -296,10 +317,10 @@ std::map const& Parser::instructionNames() return s_instructionNames; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher) { RecursionGuard recursionGuard(*this); - Statement ret; + ElementaryOperation ret; switch (currentToken()) { case Token::Identifier: @@ -402,7 +423,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::Colon); expectToken(Token::Assign); - varDecl.value.reset(new Statement(parseExpression())); + varDecl.value.reset(new Expression(parseExpression())); varDecl.location.end = locationOf(*varDecl.value).end; } else @@ -442,13 +463,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() return funDef; } -assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) +assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) { RecursionGuard recursionGuard(*this); - if (_instruction.type() == typeid(Instruction)) + if (_initialOp.type() == typeid(Instruction)) { solAssert(!m_julia, "Instructions are invalid in JULIA"); - Instruction const& instruction = std::move(boost::get(_instruction)); + Instruction& instruction = boost::get(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; ret.location = std::move(instruction.location); @@ -499,10 +520,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) expectToken(Token::RParen); return ret; } - else if (_instruction.type() == typeid(Identifier)) + else if (_initialOp.type() == typeid(Identifier)) { FunctionCall ret; - ret.functionName = std::move(boost::get(_instruction)); + ret.functionName = std::move(boost::get(_initialOp)); ret.location = ret.functionName.location; expectToken(Token::LParen); while (currentToken() != Token::RParen) diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index e46d1732e..44889a135 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -44,6 +44,8 @@ public: std::shared_ptr parse(std::shared_ptr const& _scanner); protected: + using ElementaryOperation = boost::variant; + /// Creates an inline assembly node with the given source location. template T createWithLocation(SourceLocation const& _loc = SourceLocation()) const { @@ -65,13 +67,13 @@ protected: Case parseCase(); ForLoop parseForLoop(); /// Parses a functional expression that has to push exactly one stack element - Statement parseExpression(); + assembly::Expression parseExpression(); static std::map const& instructions(); static std::map const& instructionNames(); - Statement parseElementaryOperation(bool _onlySinglePusher = false); + ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); - Statement parseCall(Statement&& _instruction); + assembly::Expression parseCall(ElementaryOperation&& _initialOp); TypedName parseTypedName(); std::string expectAsmIdentifier(); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index c72586cbb..bacd7a94e 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -102,6 +102,11 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional ")"; } +string AsmPrinter::operator()(ExpressionStatement const& _statement) +{ + return boost::apply_visitor(*this, _statement.expression); +} + string AsmPrinter::operator()(assembly::Label const& _label) { solAssert(!m_julia, ""); diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index eadf81d99..5bd87aba0 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -42,6 +42,7 @@ public: std::string operator()(assembly::Literal const& _literal); std::string operator()(assembly::Identifier const& _identifier); std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction); + std::string operator()(assembly::ExpressionStatement const& _expr); std::string operator()(assembly::Label const& _label); std::string operator()(assembly::StackAssignment const& _assignment); std::string operator()(assembly::Assignment const& _assignment); diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 0984e7d25..86f3809ce 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): m_currentScope = &scope(nullptr); } +bool ScopeFiller::operator()(ExpressionStatement const& _expr) +{ + return boost::apply_visitor(*this, _expr.expression); +} + bool ScopeFiller::operator()(Label const& _item) { if (!m_currentScope->registerLabel(_item.name)) diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index ed28abbf6..bb023f614 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -53,6 +53,7 @@ public: bool operator()(assembly::Literal const&) { return true; } bool operator()(assembly::Identifier const&) { return true; } bool operator()(assembly::FunctionalInstruction const&) { return true; } + bool operator()(assembly::ExpressionStatement const& _expr); bool operator()(assembly::Label const& _label); bool operator()(assembly::StackAssignment const&) { return true; } bool operator()(assembly::Assignment const&) { return true; } From ca0d244bf7252e76b640f88fbefd6b497a4e9d09 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 12:28:02 +0100 Subject: [PATCH 050/186] Adjust tests. --- test/libsolidity/InlineAssembly.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 82bafd49d..94e02b8f1 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope) BOOST_AUTO_TEST_CASE(if_statement_invalid) { - CHECK_PARSE_ERROR("{ if calldatasize {}", ParserError, "Instructions are not supported as conditions for if"); + CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected token \"(\""); BOOST_CHECK("{ if calldatasize() {}"); CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace"); @@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) BOOST_AUTO_TEST_CASE(switch_invalid_expression) { CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected."); - CHECK_PARSE_ERROR("{ switch calldatasize default {} }", ParserError, "Instructions are not supported as expressions for switch"); + CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected token \"(\""); CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); } @@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression) CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'"); - CHECK_PARSE_ERROR("{ for {} calldatasize {} {} }", ParserError, "Instructions are not supported as conditions for the for statement."); + CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected token \"(\""); CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); } @@ -540,7 +540,7 @@ BOOST_AUTO_TEST_CASE(function_calls) function g(a, b, c) { } - g(1, mul(2, address), f(mul(2, caller))) + g(1, mul(2, address()), f(mul(2, caller()))) y() })"; boost::replace_all(source, "\t", " "); From 2af4d7c7dd3379d8956907413258cea884c80794 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 11 Dec 2017 20:09:17 +0100 Subject: [PATCH 051/186] [SMTChecker] Keep track of current path conditions --- libsolidity/formal/SMTChecker.cpp | 35 +++++++++++++++++++++------- libsolidity/formal/SMTChecker.h | 9 +++++++ libsolidity/formal/SolverInterface.h | 5 ++++ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a22e35d64..61cb110ec 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -71,6 +71,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) m_interface->reset(); m_currentSequenceCounter.clear(); m_nextFreeSequenceCounter.clear(); + m_pathConditions.clear(); m_conditionalExecutionHappened = false; initializeLocalVariables(_function); return true; @@ -344,14 +345,14 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface->addAssertion(expr(*args[0])); + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); - m_interface->addAssertion(expr(*args[0])); + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); } } @@ -514,11 +515,11 @@ void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* { VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; - m_interface->push(); if (_condition) - m_interface->addAssertion(*_condition); + pushPathCondition(*_condition); _statement.accept(*this); - m_interface->pop(); + if (_condition) + popPathCondition(); m_conditionalExecutionHappened = true; m_currentSequenceCounter = sequenceCountersStart; @@ -533,7 +534,7 @@ void SMTChecker::checkCondition( ) { m_interface->push(); - m_interface->addAssertion(_condition); + m_interface->addAssertion(currentPathConditions() && _condition); vector expressionsToEvaluate; vector expressionNames; @@ -605,12 +606,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co return; m_interface->push(); - m_interface->addAssertion(expr(_condition)); + m_interface->addAssertion(currentPathConditions() && expr(_condition)); auto positiveResult = checkSatisifable(); m_interface->pop(); m_interface->push(); - m_interface->addAssertion(!expr(_condition)); + m_interface->addAssertion(currentPathConditions() && !expr(_condition)); auto negatedResult = checkSatisifable(); m_interface->pop(); @@ -828,3 +829,21 @@ smt::Expression SMTChecker::var(Declaration const& _decl) solAssert(m_variables.count(&_decl), ""); return m_variables.at(&_decl); } + +void SMTChecker::popPathCondition() +{ + solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); + m_pathConditions.pop_back(); +} + +void SMTChecker::pushPathCondition(smt::Expression const& _e) +{ + m_pathConditions.push_back(currentPathConditions() && _e); +} + +smt::Expression SMTChecker::currentPathConditions() +{ + if (m_pathConditions.size() == 0) + return smt::Expression(true); + return m_pathConditions.back(); +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index e7481ccaf..7f990b979 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -26,6 +26,7 @@ #include #include +#include namespace dev { @@ -145,6 +146,13 @@ private: /// The function takes one argument which is the "sequence number". smt::Expression var(Declaration const& _decl); + /// Adds a new path condition + void pushPathCondition(smt::Expression const& _e); + /// Remove the last path condition + void popPathCondition(); + /// Returns the conjunction of all path conditions or True if empty + smt::Expression currentPathConditions(); + std::shared_ptr m_interface; std::shared_ptr m_variableUsage; bool m_conditionalExecutionHappened = false; @@ -152,6 +160,7 @@ private: std::map m_nextFreeSequenceCounter; std::map m_expressions; std::map m_variables; + std::vector m_pathConditions; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 74c993e85..884873100 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -72,6 +72,11 @@ public: }, _trueValue.sort); } + static Expression implies(Expression _a, Expression _b) + { + return !std::move(_a) || std::move(_b); + } + friend Expression operator!(Expression _a) { return Expression("not", std::move(_a), Sort::Bool); From a1e296e3928b90c9a575e5e816abc566bba35493 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 13 Dec 2017 17:59:36 +0100 Subject: [PATCH 052/186] [SMTChecker] Helper functions to add an expression to the solver conjoined with or implied by the current path conditions --- libsolidity/formal/SMTChecker.cpp | 20 +++++++++++++++----- libsolidity/formal/SMTChecker.h | 4 ++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 61cb110ec..d4887a3da 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -345,14 +345,14 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); + addPathImpliedExpression(expr(*args[0])); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); - m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); + addPathImpliedExpression(expr(*args[0])); } } @@ -534,7 +534,7 @@ void SMTChecker::checkCondition( ) { m_interface->push(); - m_interface->addAssertion(currentPathConditions() && _condition); + addPathConjoinedExpression(_condition); vector expressionsToEvaluate; vector expressionNames; @@ -606,12 +606,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co return; m_interface->push(); - m_interface->addAssertion(currentPathConditions() && expr(_condition)); + addPathConjoinedExpression(expr(_condition)); auto positiveResult = checkSatisifable(); m_interface->pop(); m_interface->push(); - m_interface->addAssertion(currentPathConditions() && !expr(_condition)); + addPathConjoinedExpression(!expr(_condition)); auto negatedResult = checkSatisifable(); m_interface->pop(); @@ -847,3 +847,13 @@ smt::Expression SMTChecker::currentPathConditions() return smt::Expression(true); return m_pathConditions.back(); } + +void SMTChecker::addPathConjoinedExpression(smt::Expression const& _e) +{ + m_interface->addAssertion(currentPathConditions() && _e); +} + +void SMTChecker::addPathImpliedExpression(smt::Expression const& _e) +{ + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), _e)); +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 7f990b979..539221cce 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -152,6 +152,10 @@ private: void popPathCondition(); /// Returns the conjunction of all path conditions or True if empty smt::Expression currentPathConditions(); + /// Conjoin the current path conditions with the given parameter and add to the solver + void addPathConjoinedExpression(smt::Expression const& _e); + /// Add to the solver: the given expression implied by the current path conditions + void addPathImpliedExpression(smt::Expression const& _e); std::shared_ptr m_interface; std::shared_ptr m_variableUsage; From a6a64eb8ede9e99e67198e662fb77eee44983989 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 Dec 2017 16:42:18 +0100 Subject: [PATCH 053/186] Function grouper. --- libjulia/optimiser/FunctionGrouper.cpp | 49 +++++++++++++++ libjulia/optimiser/FunctionGrouper.h | 46 ++++++++++++++ test/libjulia/FunctionGrouper.cpp | 85 ++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 libjulia/optimiser/FunctionGrouper.cpp create mode 100644 libjulia/optimiser/FunctionGrouper.h create mode 100644 test/libjulia/FunctionGrouper.cpp diff --git a/libjulia/optimiser/FunctionGrouper.cpp b/libjulia/optimiser/FunctionGrouper.cpp new file mode 100644 index 000000000..65bf47f45 --- /dev/null +++ b/libjulia/optimiser/FunctionGrouper.cpp @@ -0,0 +1,49 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that changes the code of a black so that all non-function definition + * instructions are moved to a block of their own followed by all function definitions. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + + +void FunctionGrouper::operator()(Block& _block) +{ + vector reordered; + reordered.emplace_back(Block{_block.location, {}}); + + for (auto&& statement: _block.statements) + { + if (statement.type() == typeid(FunctionDefinition)) + reordered.emplace_back(std::move(statement)); + else + boost::get(reordered.front()).statements.emplace_back(std::move(statement)); + } + _block.statements = std::move(reordered); +} diff --git a/libjulia/optimiser/FunctionGrouper.h b/libjulia/optimiser/FunctionGrouper.h new file mode 100644 index 000000000..64a713186 --- /dev/null +++ b/libjulia/optimiser/FunctionGrouper.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that changes the code of a black so that all non-function definition + * instructions are moved to a block of their own followed by all function definitions. + */ + +#pragma once + +#include + +namespace dev +{ +namespace julia +{ + +/** + * Moves all instructions in a block into a new block at the start of the block, followed by + * all function definitions. + * + * After this step, a block is of the form + * { { I...} F... } + * Where I are (non-function-definition) instructions and F are function definitions. + */ +class FunctionGrouper +{ +public: + void operator()(Block& _block); +}; + +} +} diff --git a/test/libjulia/FunctionGrouper.cpp b/test/libjulia/FunctionGrouper.cpp new file mode 100644 index 000000000..78f382cb1 --- /dev/null +++ b/test/libjulia/FunctionGrouper.cpp @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Unit tests for the iulia function grouper. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + Block b = disambiguate(_original);\ + (FunctionGrouper{})(b);\ + string result = p(b);\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaFunctionGrouper) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ { } }"); +} + +BOOST_AUTO_TEST_CASE(single_fun) +{ + CHECK( + "{ let a:u256 function f() {} }", + "{ { let a:u256 } function f() {} }" + ); +} + +BOOST_AUTO_TEST_CASE(multi_fun_mixed) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", + "{ { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(nested_fun) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", + "{ { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(empty_block) +{ + CHECK( + "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", + "{ { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() From 99c8ed075eccebb5d8897e7a159cea08dd88fb3e Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 Dec 2017 16:42:18 +0100 Subject: [PATCH 054/186] Function hoister. --- libjulia/optimiser/FunctionHoister.cpp | 59 ++++++++++++++++++ libjulia/optimiser/FunctionHoister.h | 52 ++++++++++++++++ test/libjulia/FunctionHoister.cpp | 85 ++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 libjulia/optimiser/FunctionHoister.cpp create mode 100644 libjulia/optimiser/FunctionHoister.h create mode 100644 test/libjulia/FunctionHoister.cpp diff --git a/libjulia/optimiser/FunctionHoister.cpp b/libjulia/optimiser/FunctionHoister.cpp new file mode 100644 index 000000000..9cf67f231 --- /dev/null +++ b/libjulia/optimiser/FunctionHoister.cpp @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that changes the code so that it consists of a block starting with + * a single block followed only by function definitions and with no functions defined + * anywhere else. + */ + +#include + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; +using namespace dev::solidity; + + +void FunctionHoister::operator()(Block& _block) +{ + bool topLevel = m_isTopLevel; + m_isTopLevel = false; + for (auto&& statement: _block.statements) + { + boost::apply_visitor(*this, statement); + if (statement.type() == typeid(FunctionDefinition)) + { + m_functions.emplace_back(std::move(statement)); + statement = Block{_block.location, {}}; + } + } + auto isEmptyBlock = [](Statement const& _st) -> bool { + return _st.type() == typeid(Block) && boost::get(_st).statements.empty(); + }; + // Remove empty blocks + boost::range::remove_erase_if(_block.statements, isEmptyBlock); + if (topLevel) + _block.statements += std::move(m_functions); +} diff --git a/libjulia/optimiser/FunctionHoister.h b/libjulia/optimiser/FunctionHoister.h new file mode 100644 index 000000000..f9f8bce0a --- /dev/null +++ b/libjulia/optimiser/FunctionHoister.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimiser component that changes the code so that all function definitions are at the top + * level block. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace julia +{ + +/** + * Moves all functions to the top-level scope. + * Applying this transformation to source code that has ambiguous identifiers might + * lead to invalid code. + * + * Prerequisites: Disambiguator + */ +class FunctionHoister: public ASTModifier +{ +public: + using ASTModifier::operator(); + virtual void operator()(Block& _block); + +private: + bool m_isTopLevel = true; + std::vector m_functions; +}; + +} +} diff --git a/test/libjulia/FunctionHoister.cpp b/test/libjulia/FunctionHoister.cpp new file mode 100644 index 000000000..3d6fff858 --- /dev/null +++ b/test/libjulia/FunctionHoister.cpp @@ -0,0 +1,85 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Unit tests for the iulia function hoister. + */ + +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + Block b = disambiguate(_original);\ + (FunctionHoister{})(b);\ + string result = p(b);\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaFunctionHoister) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(single_fun) +{ + CHECK( + "{ let a:u256 function f() {} }", + "{ let a:u256 function f() {} }" + ); +} + +BOOST_AUTO_TEST_CASE(multi_fun_mixed) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", + "{ let a:u256 let c:u256 let e:u256 function f() { let b:u256 } function g() { let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(nested_fun) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", + "{ let a:u256 function g() { let c:u256 } function f() { let b:u256 let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(empty_block) +{ + CHECK( + "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", + "{ let a:u256 function f() -> x:bool { let b:u256 := 4:u256 for {} f() {} {} } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() From e2828cfa61fd059e8458bb9422295eaf50e963b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 15 Dec 2017 09:47:06 +0100 Subject: [PATCH 055/186] Favour if over switch in ABI coder. --- libsolidity/codegen/ABIFunctions.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 6648be066..00f590653 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -120,7 +120,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) Whiskers templ(R"( function (headStart, dataEnd) -> { - switch slt(sub(dataEnd, headStart), ) case 1 { revert(0, 0) } + if slt(sub(dataEnd, headStart), ) { revert(0, 0) } } )"); @@ -151,7 +151,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) R"( { let offset := (add(headStart, )) - switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } := (add(headStart, offset), dataEnd) } )" : @@ -1134,7 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from R"( // function (offset, end) -> array { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } let length := array := ((length)) let dst := array @@ -1169,7 +1169,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from else { string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize()); - templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }"); + templ("staticBoundsCheck", "if gt(add(src, mul(length, " + baseEncodedSize + ")), end) { revert(0, 0) }"); templ("retrieveElementPos", "src"); templ("baseEncodedSize", baseEncodedSize); } @@ -1197,11 +1197,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) templ = R"( // function (offset, end) -> arrayPos, length { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } length := calldataload(offset) - switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { revert(0, 0) } arrayPos := add(offset, 0x20) - switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + if gt(add(arrayPos, mul(, )), end) { revert(0, 0) } } )"; else @@ -1209,7 +1209,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) // function (offset, end) -> arrayPos { arrayPos := offset - switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + if gt(add(arrayPos, mul(, )), end) { revert(0, 0) } } )"; Whiskers w{templ}; @@ -1235,13 +1235,13 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _ Whiskers templ( R"( function (offset, end) -> array { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } let length := (offset) array := ((length)) mstore(array, length) let src := add(offset, 0x20) let dst := add(array, 0x20) - switch gt(add(src, length), end) case 1 { revert(0, 0) } + if gt(add(src, length), end) { revert(0, 0) } (src, dst, length) } )" @@ -1268,7 +1268,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr Whiskers templ(R"( // function (headStart, end) -> value { - switch slt(sub(end, headStart), ) case 1 { revert(0, 0) } + if slt(sub(end, headStart), ) { revert(0, 0) } value := () <#members> { @@ -1296,7 +1296,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr dynamic ? R"( let offset := (add(headStart, )) - switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } mstore(add(value, ), (add(headStart, offset), end)) )" : R"( @@ -1501,7 +1501,7 @@ string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type) Whiskers w(R"( function (length) -> size { // Make sure we can allocate memory without overflow - switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { revert(0, 0) } size := } @@ -1620,7 +1620,7 @@ string ABIFunctions::allocationFunction() memPtr := mload() let newFreePtr := add(memPtr, size) // protect against overflow - switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } mstore(, newFreePtr) } )") From add4cde68cd9b5c52db8a312a34591a8bb61d8fa Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Dec 2017 11:40:06 +0000 Subject: [PATCH 056/186] Populate the sourceLocation field properly in standard JSON on errors --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 4 +-- test/libsolidity/StandardCompiler.cpp | 40 ++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index e2325a0cd..361fee9c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: Bugfixes: * Parser: Disallow event declarations with no parameter list. + * Standard JSON: Populate the ``sourceLocation`` field in the error list. * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters (instead of an internal compiler error). diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ad01821eb..d44254ed9 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -81,15 +81,15 @@ Json::Value formatErrorWithException( else message = _message; + Json::Value sourceLocation; if (location && location->sourceName) { - Json::Value sourceLocation = Json::objectValue; sourceLocation["file"] = *location->sourceName; sourceLocation["start"] = location->start; sourceLocation["end"] = location->end; } - return formatError(_warning, _type, _component, message, formattedMessage, location); + return formatError(_warning, _type, _component, message, formattedMessage, sourceLocation); } set requestedContractNames(Json::Value const& _outputSelection) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 091207e8a..dd0478c55 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -234,6 +234,46 @@ BOOST_AUTO_TEST_CASE(basic_compilation) ); } +BOOST_AUTO_TEST_CASE(compilation_error) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { function }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(result.isMember("errors")); + BOOST_CHECK(result["errors"].size() >= 1); + for (auto const& error: result["errors"]) + { + BOOST_REQUIRE(error.isObject()); + BOOST_REQUIRE(error["message"].isString()); + if (error["message"].asString().find("pre-release compiler") == string::npos) + { + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(error), + "{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier, got 'RBrace'\\n" + "contract A { function }\\n ^\\n\",\"message\":\"Expected identifier, got 'RBrace'\"," + "\"severity\":\"error\",\"sourceLocation\":{\"end\":22,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}" + ); + } + } +} + BOOST_AUTO_TEST_CASE(output_selection_explicit) { char const* input = R"( From d6e73b013fc1380da07c16c49a2730969b209e4e Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 18 Dec 2017 13:31:40 +0100 Subject: [PATCH 057/186] Fix grammar: "structs type" into "struct type" --- docs/structure-of-a-contract.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 0b5548004..13925bd13 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -8,7 +8,7 @@ Structure of a Contract Contracts in Solidity are similar to classes in object-oriented languages. Each contract can contain declarations of :ref:`structure-state-variables`, :ref:`structure-functions`, -:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-structs-types` and :ref:`structure-enum-types`. +:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-struct-types` and :ref:`structure-enum-types`. Furthermore, contracts can inherit from other contracts. .. _structure-state-variables: @@ -100,9 +100,9 @@ Events are convenience interfaces with the EVM logging facilities. See :ref:`events` in contracts section for information on how events are declared and can be used from within a dapp. -.. _structure-structs-types: +.. _structure-struct-types: -Structs Types +Struct Types ============= Structs are custom defined types that can group several variables (see From 2b19bcf609eabf20913627be83ec00c153512350 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 15:44:59 +0100 Subject: [PATCH 058/186] Link to dev channel --- docs/contributing.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 0f7c3e726..a5efba8bf 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -50,8 +50,10 @@ and instead, ``git rebase`` your branch. Additionally, if you are writing a new feature, please ensure you write appropriate Boost test cases and place them under ``test/``. -However, if you are making a larger change, please consult with the Gitter -channel, first. +However, if you are making a larger change, please consult with the `Solidity Development Gitter channel +`_ (different from the one mentioned above, this on is +focused on compiler and language development instead of language use) first. + Finally, please make sure you respect the `coding standards `_ From 7755e64872ec9ff506483bb3b59af7a76368b6dc Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 18 Dec 2017 14:56:56 +0100 Subject: [PATCH 059/186] Fixed typos in comment. --- libjulia/optimiser/FunctionGrouper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libjulia/optimiser/FunctionGrouper.cpp b/libjulia/optimiser/FunctionGrouper.cpp index 65bf47f45..cc40bc46b 100644 --- a/libjulia/optimiser/FunctionGrouper.cpp +++ b/libjulia/optimiser/FunctionGrouper.cpp @@ -15,8 +15,8 @@ along with solidity. If not, see . */ /** - * Optimiser component that changes the code of a black so that all non-function definition - * instructions are moved to a block of their own followed by all function definitions. + * Optimiser component that changes the code of a block so that all non-function definition + * statements are moved to a block of their own followed by all function definitions. */ #include From b588134840bf604ed4fa6031fb06a89cbbd13bcd Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 18 Dec 2017 17:31:27 +0100 Subject: [PATCH 060/186] [SMTChecker] Fix typo in the code (satisifable->satisfiable) --- libsolidity/formal/SMTChecker.cpp | 12 ++++++------ libsolidity/formal/SMTChecker.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index d4887a3da..a85297956 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -560,7 +560,7 @@ void SMTChecker::checkCondition( } smt::CheckResult result; vector values; - tie(result, values) = checkSatisifableAndGenerateModel(expressionsToEvaluate); + tie(result, values) = checkSatisfiableAndGenerateModel(expressionsToEvaluate); string conditionalComment; if (m_conditionalExecutionHappened) @@ -607,12 +607,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co m_interface->push(); addPathConjoinedExpression(expr(_condition)); - auto positiveResult = checkSatisifable(); + auto positiveResult = checkSatisfiable(); m_interface->pop(); m_interface->push(); addPathConjoinedExpression(!expr(_condition)); - auto negatedResult = checkSatisifable(); + auto negatedResult = checkSatisfiable(); m_interface->pop(); if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR) @@ -642,7 +642,7 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co } pair> -SMTChecker::checkSatisifableAndGenerateModel(vector const& _expressionsToEvaluate) +SMTChecker::checkSatisfiableAndGenerateModel(vector const& _expressionsToEvaluate) { smt::CheckResult result; vector values; @@ -672,9 +672,9 @@ SMTChecker::checkSatisifableAndGenerateModel(vector const& _exp return make_pair(result, values); } -smt::CheckResult SMTChecker::checkSatisifable() +smt::CheckResult SMTChecker::checkSatisfiable() { - return checkSatisifableAndGenerateModel({}).first; + return checkSatisfiableAndGenerateModel({}).first; } void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 539221cce..d4c2cf6f0 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -100,9 +100,9 @@ private: std::pair> - checkSatisifableAndGenerateModel(std::vector const& _expressionsToEvaluate); + checkSatisfiableAndGenerateModel(std::vector const& _expressionsToEvaluate); - smt::CheckResult checkSatisifable(); + smt::CheckResult checkSatisfiable(); void initializeLocalVariables(FunctionDefinition const& _function); void resetVariables(std::vector _variables); From ddad6a3f80fed086bd605a6e7606b044174f3b12 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Mon, 18 Dec 2017 09:55:13 -0800 Subject: [PATCH 061/186] add clarity to destructuring assignments --- docs/control-structures.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 0c5825bc5..1a8ee25b4 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -295,6 +295,7 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di // the rest of the values are discarded. (data.length,) = f(); // Sets the length to 7 // The same can be done on the left side. + // If the tuple begins in an empty component, the beginning values are discarded. (,data[3]) = f(); // Sets data[3] to 2 // Components can only be left out at the left-hand-side of assignments, with // one exception: From bae913368a438b2de7f3be93618feb21e155055e Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Tue, 19 Dec 2017 01:44:33 -0800 Subject: [PATCH 062/186] [Docs] Include explanation of how indexed dynamic-length event args are encoded --- docs/abi-spec.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index e106d98b2..458c4ce0d 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -288,6 +288,8 @@ In effect, a log entry using this ABI is described as: - ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed); - ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above). +For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). + JSON ==== From 75dd416c6e84209f2d5fe8657a83d9e3e799fe7a Mon Sep 17 00:00:00 2001 From: Steve Waldman Date: Tue, 19 Dec 2017 03:09:13 -0800 Subject: [PATCH 063/186] [Docs] Note that events can contain both indexed and unindexed arguments for values of dynamic-length types. --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 458c4ce0d..b81e35a6f 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -288,7 +288,7 @@ In effect, a log entry using this ABI is described as: - ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed); - ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above). -For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). +For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value. JSON ==== From 241ad1ae39f9db67624e879ef1d6cc52d0472349 Mon Sep 17 00:00:00 2001 From: mekkanik Date: Wed, 20 Dec 2017 13:27:40 +0530 Subject: [PATCH 064/186] Changes to support Linux Mint: a derivative of Ubuntu. --- scripts/install_deps.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 15e864b5c..34a6328b2 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -253,19 +253,20 @@ case $(uname -s) in echo "See https://github.com/ethereum/webthree-umbrella/issues/552." exit 1 ;; - #------------------------------------------------------------------------------ # Ubuntu # #------------------------------------------------------------------------------ - Ubuntu) + Ubuntu|LinuxMint) + #LinuxMint is a distro on top of Ubuntu. #Ubuntu install_z3="" case $(lsb_release -cs) in - trusty) + trusty|qiana|rebecca|rafaela|rosa) #trusty echo "Installing solidity dependencies on Ubuntu Trusty Tahr (14.04)." + echo "Or, you may also be running Linux Mint Qiana / Rebecca / Rafaela / Rosa (base: Ubuntu Trusty Tahr (14.04).)" ;; utopic) #utopic @@ -279,9 +280,10 @@ case $(uname -s) in #wily echo "Installing solidity dependencies on Ubuntu Wily Werewolf (15.10)." ;; - xenial) + xenial|sarah|serena|sonya|sylvia) #xenial echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)." + echo "Or, you may also be running Linux Mint Sarah / Serena / Sonya / Sylvia (base: Ubuntu Xenial Xerus (16.04).)" install_z3="libz3-dev" ;; yakkety) @@ -299,6 +301,15 @@ case $(uname -s) in echo "Installing solidity dependencies on Ubuntu Artful (17.10)." install_z3="libz3-dev" ;; + betsy) + #do not try anything for betsy. + echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian." + echo "We only support Sylvia, Sonya, Serena, Sarah, Rosa, Rafaela, Rebecca, and Qiana." + echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "If you would like to get your distro working, that would be fantastic." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." + exit 1 + ;; *) #other Ubuntu echo "ERROR - Unknown or unsupported Ubuntu version (" $(lsb_release -cs) ")" From efc198d515313fffcc7a56e23b0685d9efd356db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chuck=20LeDuc=20D=C3=ADaz?= Date: Wed, 20 Dec 2017 10:48:22 +0100 Subject: [PATCH 065/186] Minor syntax and formatting changes (#3337) * Copyediting, mostly syntax. * Use consistent quote chars inside sample code comments * Revert to put back matching parenthesis * Use single backticks for comment code quotes wherever a reserved word, function or variable name is used in a comment --- docs/contracts.rst | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index a0a3009ba..fcc26b247 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -95,14 +95,14 @@ This means that cyclic creation dependencies are impossible. { // Create a new Token contract and return its address. // From the JavaScript side, the return type is simply - // "address", as this is the closest type available in + // `address`, as this is the closest type available in // the ABI. return new OwnedToken(name); } function changeName(OwnedToken tokenAddress, bytes32 name) public { - // Again, the external type of "tokenAddress" is - // simply "address". + // Again, the external type of `tokenAddress` is + // simply `address`. tokenAddress.changeName(name); } @@ -203,10 +203,10 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value contract D { function readData() public { C c = new C(); - uint local = c.f(7); // error: member "f" is not visible + uint local = c.f(7); // error: member `f` is not visible c.setData(3); local = c.getData(); - local = c.compute(3, 5); // error: member "compute" is not visible + local = c.compute(3, 5); // error: member `compute` is not visible } } @@ -308,9 +308,9 @@ inheritable properties of contracts and may be overridden by derived contracts. address owner; // This contract only defines a modifier but does not use - // it - it will be used in derived contracts. + // it: it will be used in derived contracts. // The function body is inserted where the special symbol - // "_;" in the definition of a modifier appears. + // `_;` in the definition of a modifier appears. // This means that if the owner calls this function, the // function is executed and otherwise, an exception is // thrown. @@ -321,9 +321,9 @@ inheritable properties of contracts and may be overridden by derived contracts. } contract mortal is owned { - // This contract inherits the "onlyOwner"-modifier from - // "owned" and applies it to the "close"-function, which - // causes that calls to "close" only have an effect if + // This contract inherits the `onlyOwner` modifier from + // `owned` and applies it to the `close` function, which + // causes that calls to `close` only have an effect if // they are made by the stored owner. function close() public onlyOwner { selfdestruct(owner); @@ -346,7 +346,7 @@ inheritable properties of contracts and may be overridden by derived contracts. function Register(uint initialPrice) public { price = initialPrice; } // It is important to also provide the - // "payable" keyword here, otherwise the function will + // `payable` keyword here, otherwise the function will // automatically reject all Ether sent to it. function register() public payable costs(price) { registeredAddresses[msg.sender] = true; @@ -367,7 +367,7 @@ inheritable properties of contracts and may be overridden by derived contracts. } /// This function is protected by a mutex, which means that - /// reentrant calls from within msg.sender.call cannot call f again. + /// reentrant calls from within `msg.sender.call` cannot call `f` again. /// The `return 7` statement assigns 7 to the return value but still /// executes the statement `locked = false` in the modifier. function f() public noReentrancy returns (uint) { @@ -448,7 +448,7 @@ Functions can be declared ``view`` in which case they promise not to modify the The following statements are considered modifying the state: #. Writing to state variables. -#. :ref:`Emitting events. `. +#. :ref:`Emitting events `. #. :ref:`Creating other contracts `. #. Using ``selfdestruct``. #. Sending Ether via calls. @@ -561,7 +561,7 @@ Please ensure you test your fallback function thoroughly to ensure the execution // This function is called for all messages sent to // this contract (there is no other function). // Sending Ether to this contract will cause an exception, - // because the fallback function does not have the "payable" + // because the fallback function does not have the `payable` // modifier. function() public { x = 1; } uint x; @@ -744,7 +744,7 @@ The use in the JavaScript API would be as follows: // watch for changes event.watch(function(error, result){ // result will contain various information - // including the argumets given to the Deposit + // including the argumets given to the `Deposit` // call. if (!error) console.log(result); @@ -823,7 +823,7 @@ Details are given in the following example. address owner; } - // Use "is" to derive from another contract. Derived + // Use `is` to derive from another contract. Derived // contracts can access all non-private members including // internal functions and state variables. These cannot be // accessed externally via `this`, though. @@ -846,9 +846,9 @@ Details are given in the following example. function unregister() public; } - // Multiple inheritance is possible. Note that "owned" is - // also a base class of "mortal", yet there is only a single - // instance of "owned" (as for virtual inheritance in C++). + // Multiple inheritance is possible. Note that `owned` is + // also a base class of `mortal`, yet there is only a single + // instance of `owned` (as for virtual inheritance in C++). contract named is owned, mortal { function named(bytes32 name) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); @@ -1133,7 +1133,7 @@ more advanced example to implement a set). // reference" and thus only its storage address and not // its contents is passed as part of the call. This is a // special feature of library functions. It is idiomatic - // to call the first parameter 'self', if the function can + // to call the first parameter `self`, if the function can // be seen as a method of that object. function insert(Data storage self, uint value) public @@ -1177,7 +1177,7 @@ more advanced example to implement a set). } Of course, you do not have to follow this way to use -libraries - they can also be used without defining struct +libraries: they can also be used without defining struct data types. Functions also work without any storage reference parameters, and they can have multiple storage reference parameters and in any position. @@ -1343,7 +1343,7 @@ Let us rewrite the set example from the // Here, all variables of type Set.Data have // corresponding member functions. // The following function call is identical to - // Set.insert(knownValues, value) + // `Set.insert(knownValues, value)` require(knownValues.insert(value)); } } From c5318cce09fbd42d2196b2c6cc38fb99d92142d4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 28 Dec 2017 18:59:09 +0100 Subject: [PATCH 066/186] Add artful to releases. --- scripts/release_ppa.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index cb3519b03..2dd433020 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -54,7 +54,7 @@ keyid=703F83D0 email=builds@ethereum.org packagename=solc -for distribution in trusty vivid xenial zesty +for distribution in trusty vivid xenial zesty artful do cd /tmp/ rm -rf $distribution From fdbe78a7693aef51a69ec4399de4f9b919ae1675 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 3 Jan 2018 14:32:08 +0000 Subject: [PATCH 067/186] Remove truffle patching introducing in f77480c as upstream has merged the changes (#3370) --- test/externalTests.sh | 47 ------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/test/externalTests.sh b/test/externalTests.sh index 1cc0af19e..11972eaed 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -44,53 +44,6 @@ DIR=$(mktemp -d) npm install find . -name soljson.js -exec cp "$SOLJSON" {} \; - # This is a patch that lets truffle ignore the pre-release compiler warning - cat > truffle.patch < Date: Wed, 3 Jan 2018 11:34:48 +0000 Subject: [PATCH 068/186] Properly handle colons in file names within jsonio --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 30 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 361fee9c8..dfd7aa5e4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: Bugfixes: * Parser: Disallow event declarations with no parameter list. * Standard JSON: Populate the ``sourceLocation`` field in the error list. + * Standard JSON: Properly support file names containing a colon (such as URLs). * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters (instead of an internal compiler error). diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index d44254ed9..7aa971c6f 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -461,7 +461,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value contractsOutput = Json::objectValue; for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector()) { - size_t colon = contractName.find(':'); + size_t colon = contractName.rfind(':'); solAssert(colon != string::npos, ""); string file = contractName.substr(0, colon); string name = contractName.substr(colon + 1); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index dd0478c55..fa0f1e59a 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -451,6 +451,36 @@ BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import) BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[{\"constant\":false,\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"); } +BOOST_AUTO_TEST_CASE(filename_with_colon) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "http://github.com/ethereum/solidity/std/StandardToken.sol": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "http://github.com/ethereum/solidity/std/StandardToken.sol": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "http://github.com/ethereum/solidity/std/StandardToken.sol", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + + BOOST_AUTO_TEST_SUITE_END() } From be065a1243324fb7ae937f23369746007cb9717f Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Sun, 24 Dec 2017 04:43:53 -0300 Subject: [PATCH 069/186] grammar.txt: Add optional storage location to parameters --- docs/grammar.txt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index ce3fd3ad5..e700c9465 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -27,14 +27,19 @@ ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? FunctionDefinition = 'function' Identifier? ParameterList ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' )* ( 'returns' ParameterList )? ( ';' | Block ) -EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';' +EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';' EnumValue = Identifier EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' -IndexedParameterList = '(' ( TypeName 'indexed'? Identifier? (',' TypeName 'indexed'? Identifier?)* )? ')' -ParameterList = '(' ( TypeName Identifier? (',' TypeName Identifier?)* )? ')' -TypeNameList = '(' ( TypeName (',' TypeName )* )? ')' +ParameterList = '(' ( Parameter (',' Parameter)* )? ')' +Parameter = TypeName StorageLocation? Identifier? + +EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')' +EventParameter = TypeName 'indexed'? Identifier? + +FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')' +FunctionTypeParameter = TypeName StorageLocation? // semantic restriction: mappings and structs (recursively) containing mappings // are not allowed in argument lists @@ -50,8 +55,8 @@ UserDefinedTypeName = Identifier ( '.' Identifier )* Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )* - ( 'returns' TypeNameList )? +FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* + ( 'returns' FunctionTypeParameterList )? StorageLocation = 'memory' | 'storage' StateMutability = 'pure' | 'constant' | 'view' | 'payable' From a0771691ff1a8ea8b2dda07ff50e48fc81a2a705 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 4 Jan 2018 07:24:39 -0300 Subject: [PATCH 070/186] Improve error message for wrong struct initialization (#3359) --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 6 +++++- test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 361fee9c8..026b91180 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Bugfixes: * Standard JSON: Populate the ``sourceLocation`` field in the error list. * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters (instead of an internal compiler error). + * Type Checker: Improve error message for wrong struct initialization. ### 0.4.19 (2017-11-30) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 090e5159c..75d719250 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,8 +1551,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { + bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + string msg = - "Wrong argument count for function call: " + + "Wrong argument count for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ": " + toString(arguments.size()) + " arguments given but expected " + toString(parameterTypes.size()) + diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8f58dcb15..eb6a440e1 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3608,6 +3608,20 @@ BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) CHECK_ERROR(text, TypeError, "Wrong argument count for function call: 0 arguments given but expected 1."); } +BOOST_AUTO_TEST_CASE(invalid_args_creating_struct) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + + function f() public { + var s = S({a: 1}); + } + } + )"; + CHECK_ERROR(text, TypeError, "Wrong argument count for struct constructor: 1 arguments given but expected 2."); +} + BOOST_AUTO_TEST_CASE(function_overload_array_type) { char const* text = R"( From 42cc3915feaa85ec562d9496e92b50ac59edfdb3 Mon Sep 17 00:00:00 2001 From: ChenQuan Date: Tue, 12 Dec 2017 22:47:02 +0800 Subject: [PATCH 071/186] Update GPG key URL for the Centos installer script --- scripts/install_deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 34a6328b2..b6bae6905 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -356,7 +356,7 @@ case $(uname -s) in # Make Sure we have the EPEL repos sudo yum -y install epel-release # Get g++ 4.8 - sudo rpm --import http://ftp.scientificlinux.org/linux/scientific/5x/x86_64/RPM-GPG-KEYs/RPM-GPG-KEY-cern + sudo rpm --import http://linuxsoft.cern.ch/cern/slc6X/i386/RPM-GPG-KEY-cern wget -O /etc/yum.repos.d/slc6-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo sudo yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils From 00692a4ff66551ae99920380cc4e43de377e63c7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 14:24:19 +0100 Subject: [PATCH 072/186] Reset source location after using inline assembly. --- libsolidity/codegen/CompilerContext.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ce9c3b7f6..ab10d7dd0 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -347,6 +347,9 @@ void CompilerContext::appendInlineAssembly( solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); + + // Reset the source location to the one of the node (instead of the CODEGEN source location) + updateSourceLocation(); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( From 7f4cf00f1b6a41c1fd409dbca329d7bc17f4fd56 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 14:24:35 +0100 Subject: [PATCH 073/186] Provide easy way to update source location expectation. --- test/libsolidity/Assembly.cpp | 54 +++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 358d3c720..59af6d412 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -86,23 +86,59 @@ eth::AssemblyItems compileContract(const string& _sourceCode) return AssemblyItems(); } +void printAssemblyLocations(AssemblyItems const& _items) +{ + auto printRepeated = [](SourceLocation const& _loc, size_t _repetitions) + { + cout << + "\t\tvector(" << + _repetitions << + ", SourceLocation(" << + _loc.start << + ", " << + _loc.end << + ", make_shared(\"" << + *_loc.sourceName << + "\"))) +" << endl; + }; + + vector locations; + for (auto const& item: _items) + locations.push_back(item.location()); + size_t repetitions = 0; + SourceLocation const* previousLoc = nullptr; + for (size_t i = 0; i < locations.size(); ++i) + { + SourceLocation& loc = locations[i]; + if (previousLoc && *previousLoc == loc) + repetitions++; + else + { + if (previousLoc) + printRepeated(*previousLoc, repetitions); + previousLoc = &loc; + repetitions = 1; + } + } + if (previousLoc) + printRepeated(*previousLoc, repetitions); +} + void checkAssemblyLocations(AssemblyItems const& _items, vector const& _locations) { BOOST_CHECK_EQUAL(_items.size(), _locations.size()); for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i) { - BOOST_CHECK_MESSAGE( - _items[i].location() == _locations[i], - "Location mismatch for assembly item " + to_string(i) + ". Found: " + - (_items[i].location().sourceName ? *_items[i].location().sourceName + ":" : "(null source name)") + - to_string(_items[i].location().start) + "-" + - to_string(_items[i].location().end) + ", expected: " + - (_locations[i].sourceName ? *_locations[i].sourceName + ":" : "(null source name)") + - to_string(_locations[i].start) + "-" + - to_string(_locations[i].end)); + if (_items[i].location() != _locations[i]) + { + BOOST_CHECK_MESSAGE(false, "Location mismatch for item " + to_string(i) + ". Found the following locations:"); + printAssemblyLocations(_items); + return; + } } } + } // end anonymous namespace BOOST_AUTO_TEST_SUITE(Assembly) From 6a9a4e2bb860e7e1c8bd832b251dc6877912c233 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 17:19:45 +0100 Subject: [PATCH 074/186] Explain the difference to inline assembly. --- docs/julia.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/julia.rst b/docs/julia.rst index 309e6b369..9e961a9dd 100644 --- a/docs/julia.rst +++ b/docs/julia.rst @@ -14,6 +14,13 @@ It can already be used for "inline assembly" inside Solidity and future versions of the Solidity compiler will even use JULIA as intermediate language. It should also be easy to build high-level optimizer stages for JULIA. +.. note:: + + Note that the flavour used for "inline assembly" does not have types + (everything is ``u256``) and the built-in functions are identical + to the EVM opcodes. Please resort to the inline assembly documentation + for details. + The core components of JULIA are functions, blocks, variables, literals, for-loops, if-statements, switch-statements, expressions and assignments to variables. From d0abc5359b24a27376cc1a4ba8d0b35e7ca036d2 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 18 Dec 2017 19:43:15 +0100 Subject: [PATCH 075/186] [SMTChecker] Variables are merged after branches (ite variables) --- libsolidity/formal/SMTChecker.cpp | 32 ++++++++++++++++----- libsolidity/formal/SMTChecker.h | 18 ++++++++---- test/libsolidity/SMTChecker.cpp | 47 +++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a85297956..a64024b36 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -91,15 +91,16 @@ bool SMTChecker::visit(IfStatement const& _node) checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); - visitBranch(_node.trueStatement(), expr(_node.condition())); + auto countersEndFalse = m_currentSequenceCounter; + auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); vector touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); if (_node.falseStatement()) { - visitBranch(*_node.falseStatement(), !expr(_node.condition())); + countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement()); } - resetVariables(touchedVariables); + mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse); return false; } @@ -506,12 +507,12 @@ void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& m_interface->addAssertion(newValue(_variable) == _value); } -void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) +SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) { - visitBranch(_statement, &_condition); + return visitBranch(_statement, &_condition); } -void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) +SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) { VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; @@ -522,7 +523,8 @@ void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* popPathCondition(); m_conditionalExecutionHappened = true; - m_currentSequenceCounter = sequenceCountersStart; + std::swap(sequenceCountersStart, m_currentSequenceCounter); + return sequenceCountersStart; } void SMTChecker::checkCondition( @@ -702,6 +704,22 @@ void SMTChecker::resetVariables(vector _variables) } } +void SMTChecker::mergeVariables(vector const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse) +{ + set uniqueVars(_variables.begin(), _variables.end()); + for (auto const* decl: uniqueVars) + { + int trueCounter = _countersEndTrue.at(decl); + int falseCounter = _countersEndFalse.at(decl); + solAssert(trueCounter != falseCounter, ""); + m_interface->addAssertion(newValue(*decl) == smt::Expression::ite( + _condition, + valueAtSequence(*decl, trueCounter), + valueAtSequence(*decl, falseCounter)) + ); + } +} + bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { if (dynamic_cast(_varDecl.type().get())) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index d4c2cf6f0..b57f0f96b 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -75,10 +75,14 @@ private: void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); - // Visits the branch given by the statement, pushes and pops the SMT checker. - // @param _condition if present, asserts that this condition is true within the branch. - void visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); - void visitBranch(Statement const& _statement, smt::Expression _condition); + /// Maps a variable to an SSA index. + using VariableSequenceCounters = std::map; + + /// Visits the branch given by the statement, pushes and pops the current path conditions. + /// @param _condition if present, asserts that this condition is true within the branch. + /// @returns the variable sequence counter after visiting the branch. + VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); + VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression _condition); /// Check that a condition can be satisfied. void checkCondition( @@ -106,6 +110,10 @@ private: void initializeLocalVariables(FunctionDefinition const& _function); void resetVariables(std::vector _variables); + /// Given two different branches and the touched variables, + /// merge the touched variables into after-branch ite variables + /// using the branch condition as guard. + void mergeVariables(std::vector const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse); /// Tries to create an uninitialized variable and returns true on success. /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); @@ -134,8 +142,6 @@ private: static smt::Expression minValue(IntegerType const& _t); static smt::Expression maxValue(IntegerType const& _t); - using VariableSequenceCounters = std::map; - /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); /// Creates the expression (value can be arbitrary) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 3a65aa433..2a1609cc4 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -168,9 +168,9 @@ BOOST_AUTO_TEST_CASE(function_call_does_not_clear_local_vars) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(branches_clear_variables) +BOOST_AUTO_TEST_CASE(branches_merge_variables) { - // Only clears accessed variables + // Branch does not touch variable a string text = R"( contract C { function f(uint x) public pure { @@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } )"; CHECK_SUCCESS_NO_WARNINGS(text); - // It is just a plain clear and will not combine branches. + // Positive branch touches variable a, but assertion should still hold. text = R"( contract C { function f(uint x) public pure { @@ -194,8 +194,8 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } } )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Clear also works on the else branch + CHECK_SUCCESS_NO_WARNINGS(text); + // Negative branch touches variable a, but assertion should still hold. text = R"( contract C { function f(uint x) public pure { @@ -208,8 +208,8 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } } )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Variable is not cleared, if it is only read. + CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is not merged, if it is only read. text = R"( contract C { function f(uint x) public pure { @@ -224,6 +224,36 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is reset in both branches + text = R"( + contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 3; + } + assert(a == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is reset in both branches + text = R"( + contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 4; + } + assert(a >= 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(branches_assert_condition) @@ -262,7 +292,7 @@ BOOST_AUTO_TEST_CASE(branches_assert_condition) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(ways_to_clear_variables) +BOOST_AUTO_TEST_CASE(ways_to_merge_variables) { string text = R"( contract C { @@ -275,6 +305,7 @@ BOOST_AUTO_TEST_CASE(ways_to_clear_variables) } } )"; + CHECK_WARNING(text, "Assertion violation happens here"); text = R"( contract C { function f(uint x) public pure { From e27418cb3369c8b7fa3f18ef63425ea2918b7cee Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 4 Jan 2018 21:23:56 +0100 Subject: [PATCH 076/186] [SMTChecker] Added feature line to Changelog --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 13c8118f9..bfaeeb27a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ Features: * Limit the number of warnings raised for creating abstract contracts. * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions). + * SMT Checker: If-else branch conditions are taken into account in the SMT encoding of the program + variables. Bugfixes: * Parser: Disallow event declarations with no parameter list. From 9e7e312fdfc2598d0bd43efc72738845bf2e3992 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 Jan 2018 13:24:07 +0000 Subject: [PATCH 077/186] Properly support library file names containing a colon (such as URLs). --- Changelog.md | 2 +- libsolidity/interface/StandardCompiler.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 35 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index bfaeeb27a..bad9baab3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,7 +9,7 @@ Features: Bugfixes: * Parser: Disallow event declarations with no parameter list. * Standard JSON: Populate the ``sourceLocation`` field in the error list. - * Standard JSON: Properly support file names containing a colon (such as URLs). + * Standard JSON: Properly support contract and library file names containing a colon (such as URLs). * Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters (instead of an internal compiler error). * Type Checker: Improve error message for wrong struct initialization. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 7aa971c6f..04f5bd255 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -193,7 +193,7 @@ Json::Value formatLinkReferences(std::map const& linkRefere for (auto const& ref: linkReferences) { string const& fullname = ref.second; - size_t colon = fullname.find(':'); + size_t colon = fullname.rfind(':'); solAssert(colon != string::npos, ""); string file = fullname.substr(0, colon); string name = fullname.substr(colon + 1); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index fa0f1e59a..e48624e59 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -480,6 +480,41 @@ BOOST_AUTO_TEST_CASE(filename_with_colon) BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); } +BOOST_AUTO_TEST_CASE(library_filename_with_colon) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"git:library.sol\"; contract A { function f() returns (uint) { return L.g(); } }" + }, + "git:library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["evm"]["bytecode"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); +} + BOOST_AUTO_TEST_SUITE_END() From c7632fc732dcd49bb3add1aea1d9fdc05612c034 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Fri, 5 Jan 2018 10:13:33 -0500 Subject: [PATCH 078/186] No nonsense explanation of compiler options --- docs/installing-solidity.rst | 38 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index b660cf027..4bdc284a9 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -2,9 +2,9 @@ .. _installing-solidity: -################### -Installing Solidity -################### +################################ +Installing the Solidity Compiler +################################ Versioning ========== @@ -18,30 +18,38 @@ will use the latest release. Remix ===== -If you just want to try Solidity for small contracts, you -can try `Remix `_ -which does not need any installation. If you want to use it -without connection to the Internet, you can go to -https://github.com/ethereum/browser-solidity/tree/gh-pages and -download the .ZIP file as explained on that page. +*We recommend Remix for small contracts and for quickly learning Solidity.* + +`Access Remix online `_, you don't need to install anything. +If you want to use it without connection to the Internet, go to +https://github.com/ethereum/browser-solidity/tree/gh-pages and download the .ZIP file as +explained on that page. + +Further options on this page detail installing commandline Solidity compiler software +on your computer. Choose a commandline compiler if you are working on a larger contract +or if you require more compilation options. npm / Node.js ============= -This is probably the most portable and most convenient way to install Solidity locally. +Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The +`solcjs` program has less features than all options further down this page. Our +`Using the compiler ` +documentation assumes you are using the full-featured compiler, `solc`. So if you if you +install `solcjs` from `npm` then you're on your own. -A platform-independent JavaScript library is provided by compiling the C++ source -into JavaScript using Emscripten. It can be used in projects directly (such as Remix). +Note: The `solc-js ` project is derived from the C++ +`solc` by using Emscripten. `solc-js` can be used in projects directly (such as Remix). Please refer to the `solc-js `_ repository for instructions. -It also contains a commandline tool called `solcjs`, which can be installed via npm: - .. code:: bash npm install -g solc .. note:: + The commandline is named `solcjs`. + The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`) expecting the behaviour of `solc` will not work with `solcjs`. @@ -63,7 +71,7 @@ output directories. Binary Packages =============== -Binary packages of Solidity available at +Binary packages of Solidity are available at `solidity/releases `_. We also have PPAs for Ubuntu. For the latest stable version. From a91393f4d74352be022b9a83fc3007881f770ed7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 4 Jan 2018 23:25:31 +0000 Subject: [PATCH 079/186] Support some restricted tokens (return, byte, address) as identifiers in Julia --- Changelog.md | 1 + libsolidity/inlineasm/AsmParser.cpp | 8 +++++++- test/libjulia/Parser.cpp | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index bfaeeb27a..102f2753e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Limit the number of warnings raised for creating abstract contracts. * Inline Assembly: Issue warning for using jump labels (already existed for jump instructions). + * Inline Assembly: Support some restricted tokens (return, byte, address) as identifiers in Julia mode. * SMT Checker: If-else branch conditions are taken into account in the SMT encoding of the program variables. diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 273e1d5c0..9336e620b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -566,10 +566,16 @@ string Parser::expectAsmIdentifier() string name = currentLiteral(); if (m_julia) { - if (currentToken() == Token::Bool) + switch (currentToken()) { + case Token::Return: + case Token::Byte: + case Token::Address: + case Token::Bool: advance(); return name; + default: + break; } } else if (instructions().count(name)) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 9aa325a4b..3ca62a90c 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -196,6 +196,14 @@ BOOST_AUTO_TEST_CASE(empty_call) CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); } +BOOST_AUTO_TEST_CASE(tokens_as_identifers) +{ + BOOST_CHECK(successParse("{ let return:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let byte:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let address:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); +} + BOOST_AUTO_TEST_CASE(lacking_types) { CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); From fcbdaa32b90cd7da8729411481d587151f484fa1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 6 Jan 2018 00:09:08 +0000 Subject: [PATCH 080/186] Simplify parseElementaryOperation in regards to special instructions --- libsolidity/inlineasm/AsmParser.cpp | 27 +++++++++++++++++---------- libsolidity/inlineasm/AsmParser.h | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 9336e620b..20c7b2a50 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -123,7 +123,7 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - ElementaryOperation elementary(parseElementaryOperation(false)); + ElementaryOperation elementary(parseElementaryOperation()); switch (currentToken()) { case Token::LParen: @@ -145,7 +145,7 @@ assembly::Statement Parser::parseStatement() do { expectToken(Token::Comma); - elementary = parseElementaryOperation(false); + elementary = parseElementaryOperation(); if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignemnt."); assignment.variableNames.emplace_back(boost::get(elementary)); @@ -247,10 +247,11 @@ assembly::ForLoop Parser::parseForLoop() assembly::Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - ElementaryOperation operation = parseElementaryOperation(true); + ElementaryOperation operation = parseElementaryOperation(); if (operation.type() == typeid(Instruction)) { Instruction const& instr = boost::get(operation); + // Enforce functional notation for instructions requiring multiple arguments. int args = instructionInfo(instr.instruction).args; if (args > 0 && currentToken() != Token::LParen) fatalParserError(string( @@ -260,11 +261,23 @@ assembly::Expression Parser::parseExpression() boost::lexical_cast(args) + " arguments)" )); + // Disallow instructions returning multiple values (and DUP/SWAP) as expression. + if ( + instructionInfo(instr.instruction).ret != 1 || + isDupInstruction(instr.instruction) || + isSwapInstruction(instr.instruction) + ) + fatalParserError( + "Instruction \"" + + instructionNames().at(instr.instruction) + + "\" not allowed in this context." + ); } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); else if (operation.type() == typeid(Instruction)) { + // Instructions not taking arguments are allowed as expressions. Instruction& instr = boost::get(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } @@ -317,7 +330,7 @@ std::map const& Parser::instructionNames() return s_instructionNames; } -Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher) +Parser::ElementaryOperation Parser::parseElementaryOperation() { RecursionGuard recursionGuard(*this); ElementaryOperation ret; @@ -341,12 +354,6 @@ Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePus if (!m_julia && instructions().count(literal)) { dev::solidity::Instruction const& instr = instructions().at(literal); - if (_onlySinglePusher) - { - InstructionInfo info = dev::solidity::instructionInfo(instr); - if (info.ret != 1) - fatalParserError("Instruction \"" + literal + "\" not allowed in this context."); - } ret = Instruction{location(), instr}; } else diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 44889a135..bd90bb43d 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -70,7 +70,7 @@ protected: assembly::Expression parseExpression(); static std::map const& instructions(); static std::map const& instructionNames(); - ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false); + ElementaryOperation parseElementaryOperation(); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); assembly::Expression parseCall(ElementaryOperation&& _initialOp); From 124190336b0a70ea32d5f8ca0c4b364f1fc774d0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 14:40:54 +0100 Subject: [PATCH 081/186] Split inline assembly into loose and strict flavours. --- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/codegen/CompilerContext.cpp | 9 ++++-- libsolidity/codegen/CompilerContext.h | 4 +-- libsolidity/inlineasm/AsmAnalysis.cpp | 17 +++++------ libsolidity/inlineasm/AsmAnalysis.h | 6 ++-- libsolidity/inlineasm/AsmDataForward.h | 7 +++++ libsolidity/inlineasm/AsmParser.cpp | 31 ++++++++++++--------- libsolidity/inlineasm/AsmParser.h | 8 ++++-- libsolidity/interface/AssemblyStack.cpp | 21 ++++++++++++-- test/libjulia/Common.cpp | 5 ++-- test/libjulia/Parser.cpp | 4 +-- 12 files changed, 78 insertions(+), 38 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 9eee16af4..540ffaf5a 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -207,7 +207,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations()); + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 75d719250..191f78e93 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -873,7 +873,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, - false, + assembly::AsmFlavour::Loose, identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ab10d7dd0..7a88475a5 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -319,14 +319,19 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); - auto parserResult = assembly::Parser(errorReporter).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner); #ifdef SOL_OUTPUT_ASM cout << assembly::AsmPrinter()(*parserResult) << endl; #endif assembly::AsmAnalysisInfo analysisInfo; bool analyzerResult = false; if (parserResult) - analyzerResult = assembly::AsmAnalyzer(analysisInfo, errorReporter, false, identifierAccess.resolve).analyze(*parserResult); + analyzerResult = assembly::AsmAnalyzer( + analysisInfo, + errorReporter, + assembly::AsmFlavour::Strict, + identifierAccess.resolve + ).analyze(*parserResult); if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) { string message = diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 7743fd3f0..0e8b639cd 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -187,8 +187,8 @@ public: CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; } - /// Appends inline assembly. @a _replacements are string-matching replacements that are performed - /// prior to parsing the inline assembly. + /// Appends inline assembly (strict mode). + /// @a _replacements are string-matching replacements that are performed prior to parsing the inline assembly. /// @param _localVariables assigns stack positions to variables with the last one being the stack top /// @param _system if true, this is a "system-level" assembly where all functions use named labels. void appendInlineAssembly( diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 17b7cce09..2d6e58dea 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -54,7 +54,7 @@ bool AsmAnalyzer::analyze(Block const& _block) bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); m_info.stackHeightInfo[&_label] = m_stackHeight; warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; @@ -62,7 +62,7 @@ bool AsmAnalyzer::operator()(Label const& _label) bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instruction] = m_stackHeight; @@ -141,7 +141,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(!m_julia, ""); + solAssert(m_flavour != AsmFlavour::IULIA, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) @@ -157,17 +157,18 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) { -// size_t initialStackHeight = m_stackHeight; + size_t initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); -// if (!expectDeposit(0, initialStackHeight, _statement.location)) -// success = false; + if (m_flavour != AsmFlavour::Loose) + if (!expectDeposit(0, initialStackHeight, _statement.location)) + success = false; m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; @@ -507,7 +508,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) { - if (!m_julia) + if (m_flavour != AsmFlavour::IULIA) return; if (!builtinTypes.count(type)) diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 00a33db3e..7a81dbf8b 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -54,9 +54,9 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, - bool _julia = false, + AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {} bool analyze(assembly::Block const& _block); @@ -97,7 +97,7 @@ private: std::set m_activeVariables; AsmAnalysisInfo& m_info; ErrorReporter& m_errorReporter; - bool m_julia = false; + AsmFlavour m_flavour = AsmFlavour::Loose; }; } diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index 317e257c9..3a9600fec 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -53,6 +53,13 @@ struct TypedName; using Expression = boost::variant; using Statement = boost::variant; +enum class AsmFlavour +{ + Loose, // no types, EVM instructions as function, jumps and direct stack manipulations + Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations + IULIA // same as Strict mode with types +}; + } } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 20c7b2a50..5983d7fff 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -103,14 +103,14 @@ assembly::Statement Parser::parseStatement() return parseForLoop(); case Token::Assign: { - if (m_julia) + if (m_flavour != AsmFlavour::Loose) break; assembly::StackAssignment assignment = createWithLocation(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = currentLiteral(); - if (!m_julia && instructions().count(assignment.variableName.name)) + if (instructions().count(assignment.variableName.name)) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); @@ -170,7 +170,7 @@ assembly::Statement Parser::parseStatement() if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation(identifier.location); - if (!m_julia && instructions().count(identifier.name)) + if (m_flavour != AsmFlavour::IULIA && instructions().count(identifier.name)) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); @@ -181,7 +181,7 @@ assembly::Statement Parser::parseStatement() else { // label - if (m_julia) + if (m_flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation