From 18539e53530c812790fbb396a5e5ca5806efb6ba Mon Sep 17 00:00:00 2001 From: Raghav Dua Date: Tue, 23 Jan 2018 23:23:18 +0530 Subject: [PATCH 001/242] Abstract Contracts: Add note about function type --- docs/contracts.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 273ea43ab504..69d47feff411 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,6 +1053,19 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. +Note that an abstract function is different from a :ref:`Function Type ` even though their syntax looks very similar. + +Example of Abstract function (a function declaration) +:: + + function foo(address) external returns (address); + +Example of a Function Type (a variable declaration, where the variable is of type ``function``) +:: + + function(address) external returns (address) foo; + + .. index:: ! contract;interface, ! interface contract ********** From 694fc6835f600c124bdc538fe454056aea024dc8 Mon Sep 17 00:00:00 2001 From: Raghav Dua Date: Tue, 23 Jan 2018 23:43:04 +0530 Subject: [PATCH 002/242] correct terminology --- docs/contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 69d47feff411..a9b42f1599d9 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,9 +1053,9 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. -Note that an abstract function is different from a :ref:`Function Type ` even though their syntax looks very similar. +Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. -Example of Abstract function (a function declaration) +Example of function without implementation (a function declaration) :: function foo(address) external returns (address); From 09887de266665c1233cd2aa853e8271e6af282db Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 19:02:49 +0100 Subject: [PATCH 003/242] Fix link. --- docs/contracts.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index a9b42f1599d9..d39786724d60 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1053,15 +1053,13 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. -Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. +Note that a function without implementation is different from a :ref:`Function Type ` even though their syntax looks very similar. -Example of function without implementation (a function declaration) -:: +Example of function without implementation (a function declaration):: function foo(address) external returns (address); -Example of a Function Type (a variable declaration, where the variable is of type ``function``) -:: +Example of a Function Type (a variable declaration, where the variable is of type ``function``):: function(address) external returns (address) foo; From 29d40ad42af1e7168ec3ea077a2bcfb71c68dc60 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 12:19:19 +0100 Subject: [PATCH 004/242] Clarify padding direction. --- docs/abi-spec.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index f249bbcd4a40..58b88a07ac46 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -155,15 +155,15 @@ on the type of ``X`` being ``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters. -- ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. +- ``uint``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is 32 bytes. - ``address``: as in the ``uint160`` case -- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. +- ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. - ``fixed``: as in the ``fixed128x19`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. - ``ufixed``: as in the ``ufixed128x19`` case -- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes to a length of 32. +- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes on the _right_ to a length of 32. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. From 89a8f4096c6f1cc1025d4026ebfc7b2674330458 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 19:30:30 +0100 Subject: [PATCH 005/242] Provide proper input for test call. --- test/libsolidity/SolidityEndToEndTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 4d20f4f55892..d2528a6789b0 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3565,8 +3565,8 @@ BOOST_AUTO_TEST_CASE(library_call_protection) } )"; compileAndRun(sourceCode, 0, "Lib"); - ABI_CHECK(callContractFunction("np(Lib.S storage)"), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); From a048d69c03061a2b1429a02c06a48e4d6e6e6563 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 17:20:30 +0100 Subject: [PATCH 006/242] Build documentation on CircleCI. --- .travis.yml | 14 -------------- circle.yml | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index ebe91939a61d..0b05f6610ac5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ env: global: - ENCRYPTION_LABEL="6d4541b72666" - SOLC_BUILD_TYPE=RelWithDebInfo - - SOLC_DOCS=Off - SOLC_EMSCRIPTEN=Off - SOLC_INSTALL_DEPS_TRAVIS=On - SOLC_RELEASE=On @@ -65,18 +64,6 @@ matrix: - ZIP_SUFFIX=ubuntu-trusty-clang - SOLC_STOREBYTECODE=On - # Documentation target, which generates documentation using Phoenix / ReadTheDocs. - - os: linux - dist: trusty - sudo: required - compiler: gcc - before_install: - - sudo apt-get -y install python-sphinx - env: - - SOLC_DOCS=On - - SOLC_RELEASE=Off - - SOLC_TESTS=Off - # Docker target, which generates a statically linked alpine image - os: linux dist: trusty @@ -184,7 +171,6 @@ before_script: script: - test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh) - - test $SOLC_DOCS != On || (scripts/docs.sh) - test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh) - test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh) diff --git a/circle.yml b/circle.yml index 4edc9383be92..bc72623cea99 100644 --- a/circle.yml +++ b/circle.yml @@ -101,7 +101,7 @@ jobs: - run: name: Store commit hash and prerelease command: | - date -u +"nightly.%Y.%-m.%-d" > prerelease.txt + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - restore_cache: key: ccache-{{ arch }}-{{ .Branch }} @@ -130,6 +130,28 @@ jobs: path: build/solc/solc destination: solc + docs: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install python-sphinx + - run: + name: Store commit hash and prerelease + command: | + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi + echo -n "$CIRCLE_SHA1" > commit_hash.txt + - run: + name: Build documentation + command: ./scripts/docs.sh + - store_artifacts: + path: docs/_build/html/ + destination: docs-html + workflows: version: 2 build_all: @@ -142,3 +164,4 @@ workflows: requires: - build_emscripten - build_x86 + - docs From d01786f0aa4e128f70aea2bc53a8d3c61294a587 Mon Sep 17 00:00:00 2001 From: mirgj Date: Thu, 22 Feb 2018 01:42:57 +0800 Subject: [PATCH 007/242] Update solidity-by-example.rst highestBidder is the address. it's highestBid that should be != 0 to be considered as pending return. It's that right? --- docs/solidity-by-example.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index b663083cdd5c..b8e158aceb8b 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -272,7 +272,7 @@ activate themselves. // money back. require(msg.value > highestBid); - if (highestBidder != 0) { + if (highestBid != 0) { // Sending back the money by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. From ae02bb5aadfa49636f9931524dbc026c855201f1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 4 Jan 2018 23:25:41 +0000 Subject: [PATCH 008/242] Raise error on oversized number literals in assembly --- Changelog.md | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 8 ++++++++ test/libjulia/Parser.cpp | 1 + test/libsolidity/InlineAssembly.cpp | 1 + 4 files changed, 11 insertions(+) diff --git a/Changelog.md b/Changelog.md index f25b138ba619..ad92c94a2712 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. Bugfixes: + * Assembly: Raise error on oversized number literals in assembly. * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. * Standalone Assembly: Do not ignore input after closing brace of top level block. diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2d6e58deaae6..e672d5ef4bef 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -82,6 +82,14 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } + else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1)) + { + m_errorReporter.typeError( + _literal.location, + "Number literal too large (> 256 bits)" + ); + return false; + } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 0a2dd8158b96..ff9474c1eee9 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -228,6 +228,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(builtin_types) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 70620f7800c4..45fb54f80d01 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -390,6 +390,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_PARSE_ERROR("{ let x := .1 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 1e5 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 67.235 }", ParserError, "Invalid number literal."); + CHECK_STRICT_ERROR("{ let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(function_definitions) From 42856e0f53c1cb9be61e9f24b9f09a7b0179cd55 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 22 Feb 2018 01:00:26 +0100 Subject: [PATCH 009/242] Add assertion for boolean literals in Julia-mode --- libsolidity/inlineasm/AsmAnalysis.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index e672d5ef4bef..a05ac57d5900 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -90,6 +90,11 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + solAssert(m_flavour == AsmFlavour::IULIA, ""); + solAssert(_literal.value == "true" || _literal.value == "false", ""); + } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } From 68a8e67cc37bf6f923f3b51781f2613168fa4d38 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Sep 2017 08:52:10 +0100 Subject: [PATCH 010/242] Mark functions const --- libjulia/backends/evm/EVMCodeTransform.cpp | 6 +++--- libjulia/backends/evm/EVMCodeTransform.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 0c7365fbae2a..2a97429b5161 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -522,7 +522,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } } -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) +int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const { solAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; @@ -537,12 +537,12 @@ int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& return heightDiff; } -void CodeTransform::expectDeposit(int _deposit, int _oldHeight) +void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const { solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } -void CodeTransform::checkStackHeight(void const* _astElement) +void CodeTransform::checkStackHeight(void const* _astElement) const { solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); solAssert( diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 0f2aaf955765..f8eec0b73c03 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -133,11 +133,11 @@ class CodeTransform: public boost::static_visitor<> /// 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 /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap); + int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; - void expectDeposit(int _deposit, int _oldHeight); + void expectDeposit(int _deposit, int _oldHeight) const; - void checkStackHeight(void const* _astElement); + void checkStackHeight(void const* _astElement) const; julia::AbstractAssembly& m_assembly; solidity::assembly::AsmAnalysisInfo& m_info; From c182284d28faa45e668fe20293e9e08734187ee3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 16:33:59 +0100 Subject: [PATCH 011/242] Do not warn about analysis-only experimental features. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 6 ++++-- test/libsolidity/SMTChecker.cpp | 7 +------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index ad92c94a2712..f0d004162f5a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. + * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 5a3745b0ad99..74834ba4028e 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -93,8 +93,10 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else { - m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal)); - m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + auto feature = ExperimentalFeatureNames.at(literal); + m_sourceUnit->annotation().experimentalFeatures.insert(feature); + if (!ExperimentalFeatureOnlyAnalysis.count(feature)) + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); } } } diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 8c9552928dc6..5088ab9473d6 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -35,12 +35,6 @@ namespace test class SMTCheckerFramework: public AnalysisFramework { -public: - SMTCheckerFramework() - { - m_warningsToFilter.push_back("Experimental features are turned on."); - } - protected: virtual std::pair parseAnalyseAndReturnError( @@ -103,6 +97,7 @@ BOOST_AUTO_TEST_CASE(warn_on_struct) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental feature", "Assertion checker does not yet implement this expression.", "Assertion checker does not yet support the type of this variable." })); From 388718b59f604c944be6816ba50068014e563fb0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 16:55:21 +0100 Subject: [PATCH 012/242] Introduce emit statement. --- docs/grammar.txt | 3 +- libsolidity/analysis/TypeChecker.cpp | 9 ++ libsolidity/analysis/TypeChecker.h | 1 + libsolidity/ast/AST.h | 21 ++++ libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTJsonConverter.cpp | 10 +- libsolidity/ast/ASTJsonConverter.h | 1 + libsolidity/ast/ASTPrinter.cpp | 12 +++ libsolidity/ast/ASTPrinter.h | 2 + libsolidity/ast/ASTVisitor.h | 4 + libsolidity/ast/AST_accept.h | 14 +++ libsolidity/codegen/ContractCompiler.cpp | 9 ++ libsolidity/codegen/ContractCompiler.h | 1 + libsolidity/parsing/Parser.cpp | 36 ++++++- libsolidity/parsing/Parser.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 97 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 33 +++++++ 17 files changed, 252 insertions(+), 3 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index e700c9465050..a5c2acf35262 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | SimpleStatement ) ';' + Throw | EmitStatement | SimpleStatement ) ';' ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? @@ -77,6 +77,7 @@ Continue = 'continue' Break = 'break' Return = 'return' Expression? Throw = 'throw' +EmitStatement = 'emit' FunctionCall VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )? IdentifierList = '(' ( Identifier? ',' )* Identifier? ')' diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0ee16c89e78f..d93ebbd1c1d5 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -955,6 +955,15 @@ void TypeChecker::endVisit(Return const& _return) } } +void TypeChecker::endVisit(EmitStatement const& _emit) +{ + if ( + _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event + ) + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event."); +} + bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (!_statement.initialValue()) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 344b019d4d7b..fc4ec6f067b8 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,7 @@ class TypeChecker: private ASTConstVisitor virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; virtual bool visit(Conditional const& _conditional) override; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index feffde64108f..c0d55aec45ec 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1196,6 +1196,27 @@ class Throw: public Statement virtual void accept(ASTConstVisitor& _visitor) const override; }; +/** + * The emit statement is used to emit events: emit EventName(arg1, ..., argn) + */ +class EmitStatement: public Statement +{ +public: + explicit EmitStatement( + SourceLocation const& _location, + ASTPointer const& _docString, + ASTPointer const& _functionCall + ): + Statement(_location, _docString), m_eventCall(_functionCall) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + FunctionCall const& eventCall() const { return *m_eventCall; } + +private: + ASTPointer m_eventCall; +}; + /** * Definition of a variable as a statement inside a function. It requires a type name (which can * also be "var") but the actual assignment can be missing. diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 46675e51b48d..992fe4cd0270 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -69,6 +69,7 @@ class Continue; class Break; class Return; class Throw; +class EmitStatement; class VariableDeclarationStatement; class ExpressionStatement; class Expression; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index cd9f7ecad51f..4fef67c3efac 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -540,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node) bool ASTJsonConverter::visit(Throw const& _node) { - setJsonNode(_node, "Throw", {});; + setJsonNode(_node, "Throw", {}); + return false; +} + +bool ASTJsonConverter::visit(EmitStatement const& _node) +{ + setJsonNode(_node, "EmitStatement", { + make_pair("eventCall", toJson(_node.eventCall())) + }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 9a886220f9f3..88b93699a062 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -91,6 +91,7 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 23c3cbe126e0..4273f225c77a 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node) return goDeeper(); } +bool ASTPrinter::visit(EmitStatement const& _node) +{ + writeLine("EmitStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(VariableDeclarationStatement const& _node) { writeLine("VariableDeclarationStatement"); @@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&) m_indentation--; } +void ASTPrinter::endVisit(EmitStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(VariableDeclarationStatement const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 01e4f7fcf901..de3bf8a26d11 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -76,6 +76,7 @@ class ASTPrinter: public ASTConstVisitor bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; @@ -120,6 +121,7 @@ class ASTPrinter: public ASTConstVisitor void endVisit(Break const&) override; void endVisit(Return const&) override; void endVisit(Throw const&) override; + void endVisit(EmitStatement const&) override; void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Conditional const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index b726d592dc16..b1389f0f1340 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -73,6 +73,7 @@ class ASTVisitor virtual bool visit(Break& _node) { return visitNode(_node); } virtual bool visit(Return& _node) { return visitNode(_node); } virtual bool visit(Throw& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } virtual bool visit(Conditional& _node) { return visitNode(_node); } @@ -118,6 +119,7 @@ class ASTVisitor virtual void endVisit(Break& _node) { endVisitNode(_node); } virtual void endVisit(Return& _node) { endVisitNode(_node); } virtual void endVisit(Throw& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } virtual void endVisit(Conditional& _node) { endVisitNode(_node); } @@ -175,6 +177,7 @@ class ASTConstVisitor virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } virtual bool visit(Throw const& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement const& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } virtual bool visit(Conditional const& _node) { return visitNode(_node); } @@ -220,6 +223,7 @@ class ASTConstVisitor virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } virtual void endVisit(Throw const& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Conditional const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 904d9ff65475..70ee997e15c6 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EmitStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + +void EmitStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + void ExpressionStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f463db9478db..ebb718a58687 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw) return false; } +bool ContractCompiler::visit(EmitStatement const& _emit) +{ + CompilerContext::LocationSetter locationSetter(m_context, _emit); + StackHeightChecker checker(m_context); + compileExpression(_emit.eventCall()); + checker.check(); + return false; +} + bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { StackHeightChecker checker(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 1fd80d05f3cd..d698dc71a0ad 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -109,6 +109,7 @@ class ContractCompiler: private ASTConstVisitor virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; + virtual bool visit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(PlaceholderStatement const&) override; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e306e21bf770..8c97f55fd6eb 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -897,7 +897,9 @@ ASTPointer Parser::parseStatement() case Token::Assembly: return parseInlineAssembly(docString); case Token::Identifier: - if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_scanner->currentLiteral() == "emit") + statement = parseEmitStatement(docString); + else if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); @@ -1015,6 +1017,38 @@ ASTPointer Parser::parseForStatement(ASTPointer const& ); } +ASTPointer Parser::parseEmitStatement(ASTPointer const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + m_scanner->next(); + ASTNodeFactory eventCallNodeFactory(*this); + + if (m_scanner->currentToken() != Token::Identifier) + fatalParserError("Expected event name or path."); + + vector> path; + while (true) + { + path.push_back(parseIdentifier()); + if (m_scanner->currentToken() != Token::Period) + break; + m_scanner->next(); + }; + + auto eventName = expressionFromIndexAccessStructure(path, {}); + expectToken(Token::LParen); + + vector> arguments; + vector> names; + std::tie(arguments, names) = parseFunctionCallArguments(); + eventCallNodeFactory.markEndPosition(); + nodeFactory.markEndPosition(); + expectToken(Token::RParen); + auto eventCall = eventCallNodeFactory.createNode(eventName, arguments, names); + auto statement = nodeFactory.createNode(_docString, eventCall); + return statement; +} + ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index cfdfea7e2297..3f780af90ac9 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -104,6 +104,7 @@ class Parser: public ParserBase ASTPointer parseWhileStatement(ASTPointer const& _docString); ASTPointer parseDoWhileStatement(ASTPointer const& _docString); ASTPointer parseForStatement(ASTPointer const& _docString); + ASTPointer parseEmitStatement(ASTPointer const& docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer parseSimpleStatement(ASTPointer const& _docString); ASTPointer parseVariableDeclarationStatement( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d2528a6789b0..3882e4ea6990 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2967,6 +2967,29 @@ BOOST_AUTO_TEST_CASE(event) } } +BOOST_AUTO_TEST_CASE(event_emit) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) payable { + emit Deposit(msg.sender, _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); +} + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -3009,6 +3032,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +{ + char const* sourceCode = R"( + contract A { + event x(); + } + contract B is A { + function f() returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); +} + BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( @@ -3107,6 +3152,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +{ + char const* sourceCode = R"( + contract A { + event Deposit(); + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7c03d7cbd22d..ce80ffda2c01 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7785,6 +7785,39 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(emit_events) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + emit e(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract A { event e(uint a, string b); } + contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } From 04c922e5ed038fd5f6d43a364e11b8c459898a93 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 16:57:02 +0100 Subject: [PATCH 013/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index f0d004162f5a..3af3a99d9c5d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. + * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. Bugfixes: From f58024b9744f557dbc77d5f7bfbc4319bde2e0c7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:30 +0100 Subject: [PATCH 014/242] Documentation about emitting events. --- docs/contracts.rst | 10 ++++++---- docs/introduction-to-smart-contracts.rst | 10 +++++----- docs/solidity-by-example.rst | 18 +++++++++--------- docs/structure-of-a-contract.rst | 4 ++-- docs/types.rst | 4 ++-- std/StandardToken.sol | 4 ++-- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 368b78197fa6..ee203263747b 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -724,10 +724,12 @@ All non-indexed arguments will be stored in the data part of the log. ); 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. - Deposit(msg.sender, _id, msg.value); + // Events are emitted using `emit`, followed by + // the name of the event and the arguments + // (if any) in parentheses. Any such invocation + // (even deeply nested) can be detected from + // the JavaScript API by filtering for `Deposit`. + emit Deposit(msg.sender, _id, msg.value); } } diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index c297a8adb3d8..11e072925586 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Coin { // The keyword "public" makes those variables @@ -107,7 +107,7 @@ registering with username and password - all you need is an Ethereum keypair. if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; - Sent(msg.sender, receiver, amount); + emit Sent(msg.sender, receiver, amount); } } @@ -157,10 +157,10 @@ single account. .. index:: event The line ``event Sent(address from, address to, uint amount);`` declares -a so-called "event" which is fired in the last line of the function +a so-called "event" which is emitted in the last line of the function ``send``. User interfaces (as well as server applications of course) can -listen for those events being fired on the blockchain without much -cost. As soon as it is fired, the listener will also receive the +listen for those events being emitted on the blockchain without much +cost. As soon as it is emitted, the listener will also receive the arguments ``from``, ``to`` and ``amount``, which makes it easy to track transactions. In order to listen for this event, you would use :: diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index b8e158aceb8b..e5b44c988060 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -214,7 +214,7 @@ activate themselves. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { // Parameters of the auction. Times are either @@ -282,7 +282,7 @@ activate themselves. } highestBidder = msg.sender; highestBid = msg.value; - HighestBidIncreased(msg.sender, msg.value); + emit HighestBidIncreased(msg.sender, msg.value); } /// Withdraw a bid that was overbid. @@ -325,7 +325,7 @@ activate themselves. // 2. Effects ended = true; - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); // 3. Interaction beneficiary.transfer(highestBid); @@ -371,7 +371,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract BlindAuction { struct Bid { @@ -509,7 +509,7 @@ high or low invalid bids. onlyAfter(revealEnd) { require(!ended); - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); ended = true; beneficiary.transfer(highestBid); } @@ -524,7 +524,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Purchase { uint public value; @@ -574,7 +574,7 @@ Safe Remote Purchase onlySeller inState(State.Created) { - Aborted(); + emit Aborted(); state = State.Inactive; seller.transfer(this.balance); } @@ -589,7 +589,7 @@ Safe Remote Purchase condition(msg.value == (2 * value)) payable { - PurchaseConfirmed(); + emit PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; } @@ -601,7 +601,7 @@ Safe Remote Purchase onlyBuyer inState(State.Locked) { - ItemReceived(); + emit ItemReceived(); // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index a9a7ed526040..4a0873df88d8 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -86,14 +86,14 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event function bid() public payable { // ... - HighestBidIncreased(msg.sender, msg.value); // Triggering event + emit HighestBidIncreased(msg.sender, msg.value); // Triggering event } } diff --git a/docs/types.rst b/docs/types.rst index 55eaa69ae9ea..3611bc3e6ae5 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Oracle { struct Request { @@ -481,7 +481,7 @@ Another example that uses external function types:: event NewRequest(uint); function query(bytes data, function(bytes memory) external callback) public { requests.push(Request(data, callback)); - NewRequest(requests.length - 1); + emit NewRequest(requests.length - 1); } function reply(uint requestID, bytes response) public { // Here goes the check that the reply comes from a trusted source diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 2986cb5632c7..1b218d67d1e8 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -40,7 +40,7 @@ contract StandardToken is Token { if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) { balance[_from] -= _value; balance[_to] += _value; - Transfer(_from, _to, _value); + emit Transfer(_from, _to, _value); return true; } else { return false; @@ -49,7 +49,7 @@ contract StandardToken is Token { function approve(address _spender, uint256 _value) public returns (bool success) { m_allowance[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); + emit Approval(msg.sender, _spender, _value); return true; } From 8c1a8ecc2e6518dcfee73e6cd9ee8dd9a3eaaea0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:41 +0100 Subject: [PATCH 015/242] Warn about using events without emit. --- libsolidity/analysis/TypeChecker.cpp | 10 +++++++++- libsolidity/analysis/TypeChecker.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d93ebbd1c1d5..2914472afe5f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -961,7 +961,8 @@ void TypeChecker::endVisit(EmitStatement const& _emit) _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) - m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event."); + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation."); + m_insideEmitStatement = false; } bool TypeChecker::visit(VariableDeclarationStatement const& _statement) @@ -1540,6 +1541,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); } + if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) + { + if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\"."); + else + m_errorReporter.warning(_functionCall.location(), "Invoking events without \"emit\" prefix is deprecated."); + } TypePointers parameterTypes = functionType->parameterTypes(); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index fc4ec6f067b8..16796b63f15b 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,7 @@ class TypeChecker: private ASTConstVisitor virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; @@ -131,6 +132,9 @@ class TypeChecker: private ASTConstVisitor ContractDefinition const* m_scope = nullptr; + /// Flag indicating whether we are currently inside an EmitStatement. + bool m_insideEmitStatement = false; + ErrorReporter& m_errorReporter; }; From ad010f557bc4af683688aef423e7ae68d0fd4d08 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Feb 2018 17:32:54 +0100 Subject: [PATCH 016/242] Tests for warnings. --- .../SolidityNameAndTypeResolution.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index ce80ffda2c01..27761066e2f6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7818,6 +7818,29 @@ BOOST_AUTO_TEST_CASE(emit_events) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(old_style_events_050) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_WARNING(text, "without \"emit\" prefix"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_ERROR(text, TypeError, "have to be prefixed"); +} + BOOST_AUTO_TEST_SUITE_END() } From b5a3b6a4293537c843e8387ffd0b03253160dc0a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:06:14 +0100 Subject: [PATCH 017/242] Parser test. --- test/libsolidity/SolidityParser.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 861e64083da3..b7097d0f7e20 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1708,6 +1708,19 @@ BOOST_AUTO_TEST_CASE(newInvalidTypeName) CHECK_PARSE_ERROR(text, "Expected explicit type name"); } +BOOST_AUTO_TEST_CASE(emitWithoutEvent) +{ + char const* text = R"( + contract C { + event A(); + function f() { + emit A; + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_SUITE_END() } From 2ada9683dd54a10f61816814e79f15860f8feb2e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:24:14 +0100 Subject: [PATCH 018/242] Use the term "trailing zero-bytes". --- 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 58b88a07ac46..07c8e0ce7a63 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -163,7 +163,7 @@ on the type of ``X`` being - ``fixed``: as in the ``fixed128x19`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. - ``ufixed``: as in the ``ufixed128x19`` case -- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes on the _right_ to a length of 32. +- ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. From b1acb7d226cd2819a47809e50b9591d5e0069041 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 15:35:44 +0100 Subject: [PATCH 019/242] Try external tests twice. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index bc72623cea99..eb772d9ba652 100644 --- a/circle.yml +++ b/circle.yml @@ -83,7 +83,7 @@ jobs: name: External tests command: | . /usr/local/nvm/nvm.sh - test/externalTests.sh /tmp/workspace/soljson.js + test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js build_x86: docker: - image: buildpack-deps:artful From 187e50b14c063cdfc56e085d308f2cb8dacc13bd Mon Sep 17 00:00:00 2001 From: DYLAN BECKWITH Date: Wed, 14 Feb 2018 13:09:52 -0800 Subject: [PATCH 020/242] Recommend consistent style for wrapping long lines. --- docs/style-guide.rst | 103 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4c0d44f05c03..7d513d7a8ac9 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -112,6 +112,109 @@ No:: } } +Maximum Line Length +=================== + +Keeping lines under the `PEP 8 recommendation `_ of 79 (or 99) +characters helps readers easily parse the code. + +Wrapped lines should conform to the following guidelines. + +1. The first argument should not be attached to the opening parenthesis. +2. One, and only one, indent should be used. +3. Each argument should fall on its own line. +4. The terminating element, :code:`);`, should be placed on the final line by itself. + +Function Calls + +Yes:: + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3 + ); + +No:: + + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3 + ); + + thisFunctionCallIsReallyLong( + longArgument1, + longArgument2, + longArgument3); + +Assignment Statements + +Yes:: + + thisIsALongNestedMapping[being][set][to_some_value] = someFunction( + argument1, + argument2, + argument3, + argument4 + ); + +No:: + + thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1, + argument2, + argument3, + argument4); + +Event Definitions and Event Emitters + +Yes:: + + event LongAndLotsOfArgs( + adress sender, + adress recipient, + uint256 publicKey, + uint256 amount, + bytes32[] options + ); + + LongAndLotsOfArgs( + sender, + recipient, + publicKey, + amount, + options + ); + +No:: + + event LongAndLotsOfArgs(adress sender, + adress recipient, + uint256 publicKey, + uint256 amount, + bytes32[] options); + + LongAndLotsOfArgs(sender, + recipient, + publicKey, + amount, + options); + Source File Encoding ==================== From 2ac49b3c2d10eb43deb716cdf75ae48cd64840f8 Mon Sep 17 00:00:00 2001 From: Dax Bondye Date: Thu, 22 Feb 2018 10:18:09 -0800 Subject: [PATCH 021/242] Reccomend to explicitly label the visibility of functions. --- docs/style-guide.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 7d513d7a8ac9..9249f3e1ac97 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -494,7 +494,7 @@ function body to be kept on the same line as the function declaration. The closing brace should be at the same indentation level as the function declaration. -The opening brace should be preceeded by a single space. +The opening brace should be preceded by a single space. Yes:: @@ -524,7 +524,21 @@ No:: function increment(uint x) public pure returns (uint) { return x + 1;} -The visibility modifiers for a function should come before any custom +You should explicitly label the visibility of all functions, including constructors. + +Yes:: + + function explicitlyPublic(uint val) public { + doSomething(); + } + +No:: + + function implicitlyPublic(uint val) { + doSomething(); + } + +The visibility modifier for a function should come before any custom modifiers. Yes:: From fc3473b282a0b0009c3d32660b75a3520f89b73c Mon Sep 17 00:00:00 2001 From: Hongbin Zuo Date: Fri, 23 Feb 2018 15:06:14 +0800 Subject: [PATCH 022/242] Why you made this change: Talked with Chris and Matt, in order to develop stronger ethereum community in China, we decided to initiate the activity to translate Solidity documentation into Simplified Chinese version. We have invited over 10 experienced translators as a team to contribute on this effort. What you did: I finished translation of index.rst into Simplified Chinese and wanted it to be added into main doc index. --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 3df0af3c6c76..6930afb870bc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Translations This documentation is translated into several languages by community volunteers, but the English version stands as a reference. +* `Simplified Chinese `_ * `Spanish `_ * `Russian `_ (rather outdated) From 4d1b969543c51be1188d792398e028fcbc3d85cf Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 24 Oct 2017 10:49:41 +0100 Subject: [PATCH 023/242] Use readStandardInput in the fuzzer --- test/fuzzer.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index c61410b69596..45738baab033 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -18,6 +18,7 @@ * Executable for use with AFL . */ +#include #include #include #include @@ -82,23 +83,12 @@ void testConstantOptimizer() } } -string readInput() -{ - string input; - while (!cin.eof()) - { - string s; - getline(cin, s); - input += s + '\n'; - } - return input; -} - void testStandardCompiler() { if (!quiet) cout << "Testing compiler via JSON interface." << endl; - string input = readInput(); + string input = readStandardInput(); + string outputString(compileStandard(input.c_str(), NULL)); Json::Value output; if (!jsonParseStrict(outputString, output)) @@ -125,7 +115,7 @@ void testCompiler(bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readInput(); + string input = readStandardInput(); string outputString(compileJSON(input.c_str(), optimize)); Json::Value outputJson; From 02307235c5cacc19fd7b2bc02870eea5fae55a52 Mon Sep 17 00:00:00 2001 From: Hongbin Zuo Date: Fri, 23 Feb 2018 16:44:05 +0800 Subject: [PATCH 024/242] add in progress --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 6930afb870bc..184d0e6920ce 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,7 @@ Translations This documentation is translated into several languages by community volunteers, but the English version stands as a reference. -* `Simplified Chinese `_ +* `Simplified Chinese `_ (in progress) * `Spanish `_ * `Russian `_ (rather outdated) From b80baa80021b993ba21f3afe041d5cd0fc9b015b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Feb 2018 11:54:22 +0100 Subject: [PATCH 025/242] Run EndToEnd tests on circle, too. --- circle.yml | 12 +++--------- scripts/tests.sh | 30 +++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/circle.yml b/circle.yml index eb772d9ba652..94c711b0e4d0 100644 --- a/circle.yml +++ b/circle.yml @@ -93,7 +93,7 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev + apt-get -qy install ccache cmake libboost-all-dev libz3-dev libleveldb1v5 - run: name: Init submodules command: | @@ -114,16 +114,10 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }} paths: - ~/.ccache - - run: - name: Commandline tests - command: test/cmdlineTests.sh - run: mkdir -p test_results - run: - name: Test without optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/no_opt.xml -- --no-ipc - - run: - name: Test with optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/opt.xml -- --optimize --no-ipc + name: Tests + command: scripts/tests.sh --junit_report test_results - store_test_results: path: test_results/ - store_artifacts: diff --git a/scripts/tests.sh b/scripts/tests.sh index 2b47c2547128..d414643b6c56 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -30,6 +30,20 @@ set -e REPO_ROOT="$(dirname "$0")"/.. +if [ "$1" = --junit_report ] +then + if [ -z "$2" ] + then + echo "Usage: $0 [--junit_report ]" + exit 1 + fi + testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml" + testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml" +else + testargs_no_opt='' + testargs_opt='' +fi + echo "Running commandline tests..." "$REPO_ROOT/test/cmdlineTests.sh" @@ -42,9 +56,15 @@ elif [ -z $CI ]; then ETH_PATH="eth" else mkdir -p /tmp/test - # Update hash below if binary is changed. - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth_byzantium2 - test "$(shasum /tmp/test/eth)" = "4dc3f208475f622be7c8e53bee720e14cd254c6f /tmp/test/eth" + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + if grep -i trusty /etc/lsb-release >/dev/null 2>&1 + then + ETH_BINARY=eth_byzantium2 + ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + fi + wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY + test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" sync chmod +x /tmp/test/eth sync # Otherwise we might get a "text file busy" error @@ -68,9 +88,9 @@ sleep 2 # And then run the Solidity unit-tests (once without optimization, once with), # pointing to that IPC endpoint. echo "--> Running tests without optimizer..." - "$REPO_ROOT"/build/test/soltest --show-progress -- --ipcpath /tmp/test/geth.ipc && \ + "$REPO_ROOT"/build/test/soltest --show-progress $testargs_no_opt -- --ipcpath /tmp/test/geth.ipc && \ echo "--> Running tests WITH optimizer..." && \ - "$REPO_ROOT"/build/test/soltest --show-progress -- --optimize --ipcpath /tmp/test/geth.ipc + "$REPO_ROOT"/build/test/soltest --show-progress $testargs_opt -- --optimize --ipcpath /tmp/test/geth.ipc ERROR_CODE=$? pkill "$ETH_PID" || true sleep 4 From ef7fbf8d0c7310142c39fb7a940e0732e245754a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aaron=20Cola=C3=A7o?= Date: Fri, 23 Feb 2018 23:17:48 +0530 Subject: [PATCH 026/242] Fix example; closes #3582 --- docs/frequently-asked-questions.rst | 33 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index a6bead29fd6f..3c83cbc7aeb8 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -40,9 +40,9 @@ Is there a decompiler available? ================================ There is no exact decompiler to Solidity, but -`Porosity `_ is close. -Because some information like variable names, comments, and -source code formatting is lost in the compilation process, +`Porosity `_ is close. +Because some information like variable names, comments, and +source code formatting is lost in the compilation process, it is not possible to completely recover the original source code. Bytecode can be disassembled to opcodes, a service that is provided by @@ -542,12 +542,27 @@ contract level) with ``arrayname.length = ;``. If you get the :: - int8[] memory memArr; // Case 1 - memArr.length++; // illegal - int8[5] storageArr; // Case 2 - somearray.length++; // legal - int8[5] storage storageArr2; // Explicit case 2 - somearray2.length++; // legal + // This will not compile + + pragma solidity ^0.4.18; + + contract C { + int8[] dynamicStorageArray; + int8[5] fixedStorageArray; + + function f() { + int8[] memory memArr; // Case 1 + memArr.length++; // illegal + + int8[5] storage storageArr = fixedStorageArray; // Case 2 + storageArr.length++; // illegal + + int8[] storage storageArr2 = dynamicStorageArray; + storageArr2.length++; // legal + + + } + } **Important note:** In Solidity, array dimensions are declared backwards from the way you might be used to declaring them in C or Java, but they are access as in From 917dd28eef06dd62498073fc24f8a7beb6d9c553 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 19:38:51 +0100 Subject: [PATCH 027/242] Simplify FunctionType constructors. --- libsolidity/ast/Types.cpp | 56 ++++++++++----------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e4b7e4fd8ecf..a7d3e256b040 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2153,32 +2153,19 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal m_stateMutability(_function.stateMutability()), m_declaration(&_function) { - TypePointers params; - vector paramNames; - TypePointers retParams; - vector retParamNames; - if (_isInternal && m_stateMutability == StateMutability::Payable) m_stateMutability = StateMutability::NonPayable; - params.reserve(_function.parameters().size()); - paramNames.reserve(_function.parameters().size()); for (ASTPointer const& var: _function.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - retParams.reserve(_function.returnParameters().size()); - retParamNames.reserve(_function.returnParameters().size()); for (ASTPointer const& var: _function.returnParameters()) { - retParamNames.push_back(var->name()); - retParams.push_back(var->annotation().type); + m_returnParameterNames.push_back(var->name()); + m_returnParameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(VariableDeclaration const& _varDecl): @@ -2186,16 +2173,14 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_stateMutability(StateMutability::View), m_declaration(&_varDecl) { - TypePointers paramTypes; - vector paramNames; auto returnType = _varDecl.annotation().type; while (true) { if (auto mappingType = dynamic_cast(returnType.get())) { - paramTypes.push_back(mappingType->keyType()); - paramNames.push_back(""); + m_parameterTypes.push_back(mappingType->keyType()); + m_parameterNames.push_back(""); returnType = mappingType->valueType(); } else if (auto arrayType = dynamic_cast(returnType.get())) @@ -2204,15 +2189,13 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): // Return byte arrays as as whole. break; returnType = arrayType->baseType(); - paramNames.push_back(""); - paramTypes.push_back(make_shared(256)); + m_parameterNames.push_back(""); + m_parameterTypes.push_back(make_shared(256)); } else break; } - TypePointers retParams; - vector retParamNames; if (auto structType = dynamic_cast(returnType.get())) { for (auto const& member: structType->members(nullptr)) @@ -2223,24 +2206,19 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto arrayType = dynamic_cast(member.type.get())) if (!arrayType->isByteArray()) continue; - retParams.push_back(member.type); - retParamNames.push_back(member.name); + m_returnParameterTypes.push_back(member.type); + m_returnParameterNames.push_back(member.name); } } } else { - retParams.push_back(ReferenceType::copyForLocationIfReference( + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( DataLocation::Memory, returnType )); - retParamNames.push_back(""); + m_returnParameterNames.push_back(""); } - - swap(paramTypes, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(EventDefinition const& _event): @@ -2248,17 +2226,11 @@ FunctionType::FunctionType(EventDefinition const& _event): m_stateMutability(StateMutability::NonPayable), m_declaration(&_event) { - TypePointers params; - vector paramNames; - params.reserve(_event.parameters().size()); - paramNames.reserve(_event.parameters().size()); for (ASTPointer const& var: _event.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); } FunctionType::FunctionType(FunctionTypeName const& _typeName): From 8ae6a76c58c7c35f429379fb67ccdfb457fc8e91 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 11:13:21 +0100 Subject: [PATCH 028/242] Test data location of getter return type. --- .../SolidityNameAndTypeResolution.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 27761066e2f6..93abee0d0c5b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7841,6 +7841,26 @@ BOOST_AUTO_TEST_CASE(old_style_events_050) CHECK_ERROR(text, TypeError, "have to be prefixed"); } +BOOST_AUTO_TEST_CASE(getter_is_memory_type) +{ + char const* text = R"( + contract C { + struct S { string m; } + string[] public x; + S[] public y; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + // Check that the getters return a memory strings, not a storage strings. + ContractDefinition const& c = dynamic_cast(*m_compiler.ast("").nodes().at(1)); + BOOST_CHECK(c.interfaceFunctions().size() == 2); + for (auto const& f: c.interfaceFunctions()) + { + auto const& retType = f.second->returnParameterTypes().at(0); + BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory)); + } +} + BOOST_AUTO_TEST_SUITE_END() } From 4da20bdf012d948a45cc99d137cf18ead65c0a30 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 11:07:40 +0100 Subject: [PATCH 029/242] Fix: Function types for getters should not have storage pointers. --- libsolidity/ast/Types.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a7d3e256b040..41f95f30975b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2206,7 +2206,10 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto arrayType = dynamic_cast(member.type.get())) if (!arrayType->isByteArray()) continue; - m_returnParameterTypes.push_back(member.type); + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( + DataLocation::Memory, + member.type + )); m_returnParameterNames.push_back(member.name); } } From 751705978e346b372e9aa1242c78e32892fae985 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 23 Feb 2018 18:27:27 +0100 Subject: [PATCH 030/242] Add helpers escapeIdentifier to Types --- libsolidity/ast/Types.cpp | 20 ++++++++++++++++++++ libsolidity/ast/Types.h | 8 +++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e4b7e4fd8ecf..bf5745a694b9 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -170,6 +170,26 @@ string parenthesizeUserIdentifier(string const& _internal) } +string Type::escapeIdentifier(string const& _identifier) +{ + string ret = _identifier; + boost::algorithm::replace_all(ret, "$", "_$$$_"); + boost::algorithm::replace_all(ret, ",", "_$_"); + boost::algorithm::replace_all(ret, "(", "$_"); + boost::algorithm::replace_all(ret, ")", "_$"); + return ret; +} + +string Type::unescapeIdentifier(string const& _identifier) +{ + string ret = _identifier; + boost::algorithm::replace_all(ret, "_$_", ","); + boost::algorithm::replace_all(ret, "_$$$_", "$"); + boost::algorithm::replace_all(ret, "$_", "("); + boost::algorithm::replace_all(ret, "_$", ")"); + return ret; +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2e7d05ba9c8c..ef898f379a4d 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -163,10 +163,16 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this Date: Fri, 23 Feb 2018 18:32:46 +0100 Subject: [PATCH 031/242] Rename Types::identifier to Types::richIdentifier --- libsolidity/ast/Types.cpp | 32 ++++++++++++++-------------- libsolidity/ast/Types.h | 44 ++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bf5745a694b9..97e7a92265ab 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -354,7 +354,7 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): ); } -string IntegerType::identifier() const +string IntegerType::richIdentifier() const { if (isAddress()) return "t_address"; @@ -524,7 +524,7 @@ FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPoint ); } -string FixedPointType::identifier() const +string FixedPointType::richIdentifier() const { return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits); } @@ -956,7 +956,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } -string RationalNumberType::identifier() const +string RationalNumberType::richIdentifier() const { return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); } @@ -1097,7 +1097,7 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -string StringLiteralType::identifier() const +string StringLiteralType::richIdentifier() const { // Since we have to return a valid identifier and the string itself may contain // anything, we hash it. @@ -1197,7 +1197,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } -string FixedBytesType::identifier() const +string FixedBytesType::richIdentifier() const { return "t_bytes" + std::to_string(m_bytes); } @@ -1390,7 +1390,7 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } -string ArrayType::identifier() const +string ArrayType::richIdentifier() const { string id; if (isString()) @@ -1624,7 +1624,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } -string ContractType::identifier() const +string ContractType::richIdentifier() const { return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } @@ -1776,7 +1776,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } -string StructType::identifier() const +string StructType::richIdentifier() const { return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } @@ -2008,7 +2008,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } -string EnumType::identifier() const +string EnumType::richIdentifier() const { return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } @@ -2094,7 +2094,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } -string TupleType::identifier() const +string TupleType::richIdentifier() const { return "t_tuple" + identifierList(components()); } @@ -2354,7 +2354,7 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } -string FunctionType::identifier() const +string FunctionType::richIdentifier() const { string id = "t_function_"; switch (m_kind) @@ -2847,7 +2847,7 @@ ASTPointer FunctionType::documentation() const return ASTPointer(); } -string MappingType::identifier() const +string MappingType::richIdentifier() const { return "t_mapping" + identifierList(m_keyType, m_valueType); } @@ -2870,7 +2870,7 @@ string MappingType::canonicalName() const return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")"; } -string TypeType::identifier() const +string TypeType::richIdentifier() const { return "t_type" + identifierList(actualType()); } @@ -2955,7 +2955,7 @@ u256 ModifierType::storageSize() const solAssert(false, "Storage size of non-storable type type requested."); } -string ModifierType::identifier() const +string ModifierType::richIdentifier() const { return "t_modifier" + identifierList(m_parameterTypes); } @@ -2984,7 +2984,7 @@ string ModifierType::toString(bool _short) const return name + ")"; } -string ModuleType::identifier() const +string ModuleType::richIdentifier() const { return "t_module_" + std::to_string(m_sourceUnit.id()); } @@ -3010,7 +3010,7 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } -string MagicType::identifier() const +string MagicType::richIdentifier() const { switch (m_kind) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ef898f379a4d..a12b7063e472 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -163,7 +163,13 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this const& _types = std::vector()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -972,7 +978,7 @@ class FunctionType: public Type /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -1076,7 +1082,7 @@ class MappingType: public Type MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; @@ -1113,7 +1119,7 @@ class TypeType: public Type TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1141,7 +1147,7 @@ class ModifierType: public Type virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1162,7 +1168,7 @@ class ModuleType: public Type explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1192,7 +1198,7 @@ class MagicType: public Type return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1216,7 +1222,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } - virtual std::string identifier() const override { return "t_inaccessible"; } + virtual std::string richIdentifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } From b471983e3cacf4d92ecd987eac85620bd8030aff Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 24 Feb 2018 01:13:34 +0100 Subject: [PATCH 032/242] Use new escaping helpers for type identifiers --- libsolidity/ast/Types.cpp | 27 +++++++++------------------ libsolidity/ast/Types.h | 2 -- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 97e7a92265ab..fadaf62157db 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -131,28 +131,28 @@ namespace string parenthesizeIdentifier(string const& _internal) { - return "$_" + _internal + "_$"; + return "(" + _internal + ")"; } template string identifierList(Range const&& _list) { - return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); + return parenthesizeIdentifier(boost::algorithm::join(_list, ",")); } -string identifier(TypePointer const& _type) +string richIdentifier(TypePointer const& _type) { - return _type ? _type->identifier() : ""; + return _type ? _type->richIdentifier() : ""; } string identifierList(vector const& _list) { - return identifierList(_list | boost::adaptors::transformed(identifier)); + return identifierList(_list | boost::adaptors::transformed(richIdentifier)); } string identifierList(TypePointer const& _type) { - return parenthesizeIdentifier(identifier(_type)); + return parenthesizeIdentifier(richIdentifier(_type)); } string identifierList(TypePointer const& _type1, TypePointer const& _type2) @@ -165,7 +165,7 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(_internal); } } @@ -173,23 +173,14 @@ string parenthesizeUserIdentifier(string const& _internal) string Type::escapeIdentifier(string const& _identifier) { string ret = _identifier; - boost::algorithm::replace_all(ret, "$", "_$$$_"); + // FIXME: should be _$$$_ + boost::algorithm::replace_all(ret, "$", "$$$"); boost::algorithm::replace_all(ret, ",", "_$_"); boost::algorithm::replace_all(ret, "(", "$_"); boost::algorithm::replace_all(ret, ")", "_$"); return ret; } -string Type::unescapeIdentifier(string const& _identifier) -{ - string ret = _identifier; - boost::algorithm::replace_all(ret, "_$_", ","); - boost::algorithm::replace_all(ret, "_$$$_", "$"); - boost::algorithm::replace_all(ret, "$_", "("); - boost::algorithm::replace_all(ret, "_$", ")"); - return ret; -} - TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index a12b7063e472..7985521e2b26 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -176,8 +176,6 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this Date: Mon, 26 Feb 2018 16:25:23 +0100 Subject: [PATCH 033/242] Add test for escapeIdentifier --- test/libsolidity/SolidityTypes.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 9f385a04dcf4..bc9f2fe17281 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -88,6 +88,21 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_escaping) +{ + BOOST_CHECK_EQUAL(Type::escapeIdentifier("("), "$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(")"), "_$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(","), "_$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("$"), "$$$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("()"), "$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,)"), "$__$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,$,)"), "$__$_$$$_$__$"); + BOOST_CHECK_EQUAL( + Type::escapeIdentifier("((__(_$_$$,__($$,,,$$),$,,,)))$$,$$"), + "$_$___$__$$$_$$$$$$_$___$_$$$$$$_$__$__$_$$$$$$_$_$_$$$_$__$__$__$_$_$$$$$$$_$_$$$$$$" + ); +} + BOOST_AUTO_TEST_CASE(type_identifiers) { ASTNode::resetID(); From b9dccf9f20d8f4348966e3c6cacd11fbda47c162 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:33:03 +0100 Subject: [PATCH 034/242] Split circle into build and test for x86. --- circle.yml | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 94c711b0e4d0..add8a81547e0 100644 --- a/circle.yml +++ b/circle.yml @@ -93,7 +93,7 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev libleveldb1v5 + apt-get -qy install ccache cmake libboost-all-dev libz3-dev - run: name: Init submodules command: | @@ -114,15 +114,34 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }} paths: - ~/.ccache + - store_artifacts: + path: build/solc/solc + destination: solc + - persist_to_workspace: + root: build + paths: + - solc/solc + - test/soltest + - test/solfuzzer + + test_x86: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Install dependencies + command: | + apt-get -qq update + apt-get -qy install libz3-dev libleveldb1v5 - run: mkdir -p test_results - run: name: Tests command: scripts/tests.sh --junit_report test_results - store_test_results: path: test_results/ - - store_artifacts: - path: build/solc/solc - destination: solc docs: docker: @@ -158,4 +177,7 @@ workflows: requires: - build_emscripten - build_x86 + - test_x86: + requires: + - build_x86 - docs From 64eaff64200d166bdd48f81bceefec9bc83db72f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 24 Nov 2017 03:06:42 +0000 Subject: [PATCH 035/242] Random documentation updates (assembly, faq) --- docs/assembly.rst | 14 ++++++++------ docs/contracts.rst | 6 +++--- docs/frequently-asked-questions.rst | 30 ----------------------------- 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 02522469ee73..35484ad48aa3 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -162,6 +162,8 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ +| Instruction | | Explanation | ++=========================+======+=================================================================+ | stop + `-` | stop execution, identical to return(0,0) | +-------------------------+------+-----------------------------------------------------------------+ | add(x, y) | | x + y | @@ -228,7 +230,7 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ | mstore(p, v) | `-` | mem[p..(p+32)) := v | +-------------------------+------+-----------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte | +| mstore8(p, v) | `-` | mem[p] := v & 0xff (only modifies a single byte) | +-------------------------+------+-----------------------------------------------------------------+ | sload(p) | | storage[p] | +-------------------------+------+-----------------------------------------------------------------+ @@ -242,7 +244,7 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+------+-----------------------------------------------------------------+ | balance(a) | | wei balance at address a | +-------------------------+------+-----------------------------------------------------------------+ -| caller | | call sender (excluding delegatecall) | +| caller | | call sender (excluding ``delegatecall``) | +-------------------------+------+-----------------------------------------------------------------+ | callvalue | | wei sent together with the current call | +-------------------------+------+-----------------------------------------------------------------+ @@ -276,13 +278,13 @@ In the grammar, opcodes are represented as pre-defined identifiers. | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | | | | and 1 on success | +-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay | +| callcode(g, a, v, in, | | identical to ``call`` but only use the code from a and stay | | insize, out, outsize) | | in the context of the current contract otherwise | +-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` | +| delegatecall(g, a, in, | | identical to ``callcode`` but also keep ``caller`` | | insize, out, outsize) | | and ``callvalue`` | +-------------------------+------+-----------------------------------------------------------------+ -| staticcall(g, a, in, | | identical to `call(g, a, 0, in, insize, out, outsize)` but do | +| staticcall(g, a, in, | | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | | insize, out, outsize) | | not allow state modifications | +-------------------------+------+-----------------------------------------------------------------+ | return(p, s) | `-` | end execution, return data mem[p..(p+s)) | @@ -727,7 +729,7 @@ The following assembly will be generated:: // function dispatcher switch div(calldataload(0), exp(2, 226)) case 0xb3de648b { - let (r) = f(calldataload(4)) + let r := f(calldataload(4)) let ret := $allocate(0x20) mstore(ret, r) return(ret, 0x20) diff --git a/docs/contracts.rst b/docs/contracts.rst index ee203263747b..6dfcdf601823 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -467,13 +467,13 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` is an alias to ``view``. + ``constant`` on functions is an alias to ``view``. .. note:: Getter methods are marked ``view``. .. warning:: - The compiler does not enforce yet that a ``view`` method is not modifying state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state. .. index:: ! pure function, function;pure @@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo } .. warning:: - The compiler does not enforce yet that a ``pure`` method is not reading from the state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state. .. index:: ! fallback function, function;fallback diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 3c83cbc7aeb8..6a2fe6859812 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -9,17 +9,6 @@ This list was originally compiled by `fivedogit `_. Basic Questions *************** -Example contracts -================= - -There are some `contract examples `_ by fivedogit and -there should be a `test contract `_ for every single feature of Solidity. - -Create and publish the most basic contract possible -=================================================== - -A quite simple contract is the `greeter `_ - Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction) ============================================================================================================= @@ -74,25 +63,6 @@ has it (which includes `Remix `_), then ``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. -Store Ether in a contract -========================= - -The trick is to create the contract with ``{from:someaddress, value: web3.toWei(3,"ether")...}`` - -See `endowment_retriever.sol `_. - -Use a non-constant function (req ``sendTransaction``) to increment a variable in a contract -=========================================================================================== - -See `value_incrementer.sol `_. - -Get a contract to return its funds to you (not using ``selfdestruct(...)``). -============================================================================ - -This example demonstrates how to send funds from a contract to an address. - -See `endowment_retriever `_. - Can you return an array or a ``string`` from a solidity function call? ====================================================================== From 098033c94a6bbedaee60b19f096dfee5bf2b4550 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 16:58:56 +0100 Subject: [PATCH 036/242] Fix name shadowing in ballot example contract. --- docs/solidity-by-example.rst | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index e5b44c988060..57556fa52dea 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -94,7 +94,11 @@ of votes. // called incorrectly. But watch out, this // will currently also consume all provided gas // (this is planned to change in the future). - require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)); + require( + (msg.sender == chairperson) && + !voters[voter].voted && + (voters[voter].weight == 0) + ); voters[voter].weight = 1; } @@ -126,15 +130,15 @@ of votes. // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; - Voter storage delegate = voters[to]; - if (delegate.voted) { + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes - proposals[delegate.vote].voteCount += sender.weight; + proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. - delegate.weight += sender.weight; + delegate_.weight += sender.weight; } } @@ -155,13 +159,13 @@ of votes. /// @dev Computes the winning proposal taking all /// previous votes into account. function winningProposal() public view - returns (uint winningProposal) + returns (uint winningProposal_) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; - winningProposal = p; + winningProposal_ = p; } } } @@ -170,12 +174,13 @@ of votes. // of the winner contained in the proposals array and then // returns the name of the winner function winnerName() public view - returns (bytes32 winnerName) + returns (bytes32 winnerName_) { - winnerName = proposals[winningProposal()].name; + winnerName_ = proposals[winningProposal()].name; } } + Possible Improvements ===================== From 92fe9e621eace7527818ac1a136603d15058cb87 Mon Sep 17 00:00:00 2001 From: wbt Date: Tue, 23 Jan 2018 11:55:37 -0500 Subject: [PATCH 037/242] Note deprecation of constant keyword on functions --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 6dfcdf601823..416dc6499bce 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -467,7 +467,7 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` on functions is an alias to ``view``. + ``constant`` on functions is an alias to ``view``, but this is deprecated and is planned to be dropped in version 0.5.0. .. note:: Getter methods are marked ``view``. From 241134a8816fbd832ab60ccd7aecc70ba00e8ac3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 00:36:01 +0100 Subject: [PATCH 038/242] Mention that solcjs is not compatible with solc in the using-the-compiler section --- docs/installing-solidity.rst | 2 ++ docs/using-the-compiler.rst | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 8f30f19972bd..8c445e5ef00a 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -29,6 +29,8 @@ Further options on this page detail installing commandline Solidity compiler sof on your computer. Choose a commandline compiler if you are working on a larger contract or if you require more compilation options. +.. _solcjs: + npm / Node.js ============= diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 42cc807aa33c..66e3ac35eba9 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -9,6 +9,9 @@ Using the compiler Using the Commandline Compiler ****************************** +.. note:: + This section doesn't apply to :ref:`solcjs `. + One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. From 3594f02d51aac4a99d3f1fefbd37fa27346b75ae Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 00:38:52 +0100 Subject: [PATCH 039/242] Fix link to solcjs in the installation section --- docs/installing-solidity.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 8c445e5ef00a..e26870f03912 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -36,13 +36,13 @@ npm / Node.js 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 +:ref:`commandline-compiler` documentation assumes you are using the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will -stop reading the documentation here and then continue to , +stop reading the documentation here and then continue to `solc-js `_. -Note: The `solc-js ` project is derived from the C++ +Note: The solc-js project is derived from the C++ `solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix). -Please refer to the `solc-js `_ repository for instructions. +Please refer to the solc-js repository for instructions. .. code:: bash From 5c0d82059f50cb6ac67171ca075f394acae27228 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 02:10:24 +0100 Subject: [PATCH 040/242] Turn throw into a syntax error for 0.5.0 --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 16 ++++++++++++---- .../SolidityNameAndTypeResolution.cpp | 11 ++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3af3a99d9c5d..acd3aa54195b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. + * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 74834ba4028e..1dcfeb27142a 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -174,10 +174,18 @@ bool SyntaxChecker::visit(Break const& _breakStatement) bool SyntaxChecker::visit(Throw const& _throwStatement) { - m_errorReporter.warning( - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + if (v050) + m_errorReporter.syntaxError( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); + else + m_errorReporter.warning( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); return true; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 93abee0d0c5b..be147e48f0a2 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6332,7 +6332,16 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) } } )"; - CHECK_WARNING(text, "\"throw\" is deprecated"); + CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + throw; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); } BOOST_AUTO_TEST_CASE(bare_revert) From e34d367593ccc1d5b7456dfc171473997a645eb6 Mon Sep 17 00:00:00 2001 From: Dax Bondye Date: Fri, 23 Feb 2018 10:10:01 -0800 Subject: [PATCH 041/242] Multiline output parameters and return statements --- docs/style-guide.rst | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 9249f3e1ac97..533c4be57e31 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -112,7 +112,9 @@ No:: } } -Maximum Line Length +.. _maximum_line_length: + +Maximum Line Length =================== Keeping lines under the `PEP 8 recommendation `_ of 79 (or 99) @@ -650,6 +652,50 @@ No:: doSomething(); } +Multiline output parameters and return statements should follow the same style recommended for wrapping long lines found in the :ref:`Maximum Line Length ` section. + +Yes:: + + function thisFunctionNameIsReallyLong( + address a, + address b, + address c + ) + public + returns ( + address someAddressName, + uint256 LongArgument, + uint256 Argument + ) + { + doSomething() + + return ( + veryLongReturnArg1, + veryLongReturnArg2, + veryLongReturnArg3 + ); + } + +No:: + + function thisFunctionNameIsReallyLong( + address a, + address b, + address c + ) + public + returns (address someAddressName, + uint256 LongArgument, + uint256 Argument) + { + doSomething() + + return (veryLongReturnArg1, + veryLongReturnArg1, + veryLongReturnArg1); + } + For constructor functions on inherited contracts whose bases require arguments, it is recommended to drop the base constructors onto new lines in the same manner as modifiers if the function declaration is long or hard to read. From e424bd1007343d2e39b1949e6195fc1f0698441c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 26 Feb 2018 23:59:44 +0100 Subject: [PATCH 042/242] Fix some keyword highlighting in docs --- docs/assembly.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 35484ad48aa3..d7e5eaf01679 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -378,8 +378,8 @@ Functions external to inline assembly can also be accessed: The assembly will push their entry label (with virtual function resolution applied). The calling semantics in solidity are: - - the caller pushes return label, arg1, arg2, ..., argn - - the call returns with ret1, ret2, ..., retm + - the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn`` + - the call returns with ``ret1``, ``ret2``, ..., ``retm`` This feature is still a bit cumbersome to use, because the stack offset essentially changes during the call, and thus references to local variables will be wrong. From 8cebfb168bdd9c17769670f289d7721538fd1230 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 01:19:38 +0100 Subject: [PATCH 043/242] Introduce VM version ('hard fork') column for assembly opcodes --- docs/assembly.rst | 324 +++++++++++++++++++++++----------------------- 1 file changed, 163 insertions(+), 161 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index d7e5eaf01679..46416142776d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -153,6 +153,8 @@ If an opcode takes arguments (always from the top of the stack), they are given Note that the order of arguments can be seen to be reversed in non-functional style (explained below). Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are special and all others push exactly one item onto the stack. +Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively. +Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to (excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. @@ -161,167 +163,167 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. In the grammar, opcodes are represented as pre-defined identifiers. -+-------------------------+------+-----------------------------------------------------------------+ -| Instruction | | Explanation | -+=========================+======+=================================================================+ -| stop + `-` | stop execution, identical to return(0,0) | -+-------------------------+------+-----------------------------------------------------------------+ -| add(x, y) | | x + y | -+-------------------------+------+-----------------------------------------------------------------+ -| sub(x, y) | | x - y | -+-------------------------+------+-----------------------------------------------------------------+ -| mul(x, y) | | x * y | -+-------------------------+------+-----------------------------------------------------------------+ -| div(x, y) | | x / y | -+-------------------------+------+-----------------------------------------------------------------+ -| sdiv(x, y) | | x / y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| mod(x, y) | | x % y | -+-------------------------+------+-----------------------------------------------------------------+ -| smod(x, y) | | x % y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| exp(x, y) | | x to the power of y | -+-------------------------+------+-----------------------------------------------------------------+ -| not(x) | | ~x, every bit of x is negated | -+-------------------------+------+-----------------------------------------------------------------+ -| lt(x, y) | | 1 if x < y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| gt(x, y) | | 1 if x > y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| eq(x, y) | | 1 if x == y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| iszero(x) | | 1 if x == 0, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| and(x, y) | | bitwise and of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| or(x, y) | | bitwise or of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| xor(x, y) | | bitwise xor of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte | -+-------------------------+------+-----------------------------------------------------------------+ -| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant | -+-------------------------+------+-----------------------------------------------------------------+ -| keccak256(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| sha3(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| jump(label) | `-` | jump to label / code position | -+-------------------------+------+-----------------------------------------------------------------+ -| jumpi(label, cond) | `-` | jump to label if cond is nonzero | -+-------------------------+------+-----------------------------------------------------------------+ -| pc | | current position in code | -+-------------------------+------+-----------------------------------------------------------------+ -| pop(x) | `-` | remove the element pushed by x | -+-------------------------+------+-----------------------------------------------------------------+ -| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | -+-------------------------+------+-----------------------------------------------------------------+ -| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it | -+-------------------------+------+-----------------------------------------------------------------+ -| mload(p) | | mem[p..(p+32)) | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore(p, v) | `-` | mem[p..(p+32)) := v | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff (only modifies a single byte) | -+-------------------------+------+-----------------------------------------------------------------+ -| sload(p) | | storage[p] | -+-------------------------+------+-----------------------------------------------------------------+ -| sstore(p, v) | `-` | storage[p] := v | -+-------------------------+------+-----------------------------------------------------------------+ -| msize | | size of memory, i.e. largest accessed memory index | -+-------------------------+------+-----------------------------------------------------------------+ -| gas | | gas still available to execution | -+-------------------------+------+-----------------------------------------------------------------+ -| address | | address of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| balance(a) | | wei balance at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| caller | | call sender (excluding ``delegatecall``) | -+-------------------------+------+-----------------------------------------------------------------+ -| callvalue | | wei sent together with the current call | -+-------------------------+------+-----------------------------------------------------------------+ -| calldataload(p) | | call data starting from position p (32 bytes) | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatasize | | size of call data in bytes | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| codesize | | size of the code of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodesize(a) | | size of the code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatasize | | size of the last returndata | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei | -| | | and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address | -| | | keccak256(
. n . keccak256(mem[p..(p+s))) and send v | -| | | wei and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) | -| insize, out, outsize) | | providing g gas and v wei and output area | -| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | -| | | and 1 on success | -+-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to ``call`` but only use the code from a and stay | -| insize, out, outsize) | | in the context of the current contract otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to ``callcode`` but also keep ``caller`` | -| insize, out, outsize) | | and ``callvalue`` | -+-------------------------+------+-----------------------------------------------------------------+ -| staticcall(g, a, in, | | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | -| insize, out, outsize) | | not allow state modifications | -+-------------------------+------+-----------------------------------------------------------------+ -| return(p, s) | `-` | end execution, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a | -+-------------------------+------+-----------------------------------------------------------------+ -| invalid | `-` | end execution with invalid instruction | -+-------------------------+------+-----------------------------------------------------------------+ -| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | -| t4) | | | -+-------------------------+------+-----------------------------------------------------------------+ -| origin | | transaction sender | -+-------------------------+------+-----------------------------------------------------------------+ -| gasprice | | gas price of the transaction | -+-------------------------+------+-----------------------------------------------------------------+ -| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current | -+-------------------------+------+-----------------------------------------------------------------+ -| coinbase | | current mining beneficiary | -+-------------------------+------+-----------------------------------------------------------------+ -| timestamp | | timestamp of the current block in seconds since the epoch | -+-------------------------+------+-----------------------------------------------------------------+ -| number | | current block number | -+-------------------------+------+-----------------------------------------------------------------+ -| difficulty | | difficulty of the current block | -+-------------------------+------+-----------------------------------------------------------------+ -| gaslimit | | block gas limit of the current block | -+-------------------------+------+-----------------------------------------------------------------+ ++-------------------------+-----+---+-----------------------------------------------------------------+ +| Instruction | | | Explanation | ++=========================+=====+===+=================================================================+ +| stop + `-` | F | stop execution, identical to return(0,0) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| add(x, y) | | F | x + y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sub(x, y) | | F | x - y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mul(x, y) | | F | x * y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| div(x, y) | | F | x / y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sdiv(x, y) | | F | x / y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mod(x, y) | | F | x % y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| smod(x, y) | | F | x % y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| exp(x, y) | | F | x to the power of y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| not(x) | | F | ~x, every bit of x is negated | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| lt(x, y) | | F | 1 if x < y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gt(x, y) | | F | 1 if x > y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| eq(x, y) | | F | 1 if x == y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| iszero(x) | | F | 1 if x == 0, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| and(x, y) | | F | bitwise and of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| or(x, y) | | F | bitwise or of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| xor(x, y) | | F | bitwise xor of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| keccak256(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sha3(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jump(label) | `-` | F | jump to label / code position | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pc | | F | current position in code | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pop(x) | `-` | F | remove the element pushed by x | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mload(p) | | F | mem[p..(p+32)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore(p, v) | `-` | F | mem[p..(p+32)) := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sload(p) | | F | storage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sstore(p, v) | `-` | F | storage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| msize | | F | size of memory, i.e. largest accessed memory index | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gas | | F | gas still available to execution | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| address | | F | address of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| balance(a) | | F | wei balance at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| caller | | F | call sender (excluding ``delegatecall``) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callvalue | | F | wei sent together with the current call | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldataload(p) | | F | call data starting from position p (32 bytes) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatasize | | F | size of call data in bytes | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codesize | | F | size of the code of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodesize(a) | | F | size of the code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatasize | | B | size of the last returndata | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei | +| | | | and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address | +| | | | keccak256(
. n . keccak256(mem[p..(p+s))) and send v | +| | | | wei and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) | +| insize, out, outsize) | | | providing g gas and v wei and output area | +| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | +| | | | and 1 on success | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay | +| insize, out, outsize) | | | in the context of the current contract otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` | +| insize, out, outsize) | | | and ``callvalue`` | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | +| insize, out, outsize) | | | not allow state modifications | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| invalid | `-` | F | end execution with invalid instruction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| t4) | | | | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| origin | | F | transaction sender | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gasprice | | F | gas price of the transaction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| coinbase | | F | current mining beneficiary | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| timestamp | | F | timestamp of the current block in seconds since the epoch | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| number | | F | current block number | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| difficulty | | F | difficulty of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gaslimit | | F | block gas limit of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ Literals -------- From aeb75172d584c45fcf484b99ad3495d5774850dd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 01:43:12 +0100 Subject: [PATCH 044/242] Fix builtin function formatting in julia --- docs/julia.rst | 153 +++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/docs/julia.rst b/docs/julia.rst index 9e961a9ddd43..078bc55bdca1 100644 --- a/docs/julia.rst +++ b/docs/julia.rst @@ -320,168 +320,169 @@ The following functions must be available: +---------------------------------------------------------------------------------------------------------------+ | *Arithmetics* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addu256(x:u256, y:u256) -> z:u256 | x + y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | subu256(x:u256, y:u256) -> z:u256 | x - y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulu256(x:u256, y:u256) -> z:u256 | x * y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divu256(x:u256, y:u256) -> z:u256 | x / y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | modu256(x:u256, y:u256) -> z:u256 | x % y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | expu256(x:u256, y:u256) -> z:u256 | x to the power of y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | notu256(x:u256) -> z:u256 | ~x, every bit of x is negated | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | -| Cannot this be just replaced by and256(shr256(n, x), 0xff) and let it be optimised out by the EVM backend? | -+---------------------------------------------------------------------------------------------------------------+ +| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | +| | let it be optimised out by the EVM backend? | ++---------------------------------------------+-----------------------------------------------------------------+ | *Memory and storage* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mload(p:u256) -> v:u256 | mem[p..(p+32)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore(p:u256, v:u256) | mem[p..(p+32)) := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sload(p:u256) -> v:u256 | storage[p] | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sstore(p:u256, v:u256) | storage[p] := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due | | | due to the memory extension function, which extends by words, | | | this will always be a multiple of 32 bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Execution control* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | | | and return the new address | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | | insize:u256, out:u256, | providing g gas and v wei and output area | | outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | | -> r:u256 | and 1 on success | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a | | insize:u256, out:u256, | and stay in the context of the | | outsize:u256) -> r:u256 | current contract otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, | | insize:u256, out:u256, | but also keep ``caller`` | | outsize:u256) -> r:u256 | and ``callvalue`` | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | stop() | stop execution, identical to return(0,0) | -| Perhaps it would make sense retiring this as it equals to return(0,0). It can be an optimisation by the EVM | -| backend. | -+---------------------------------------------------------------------------------------------------------------+ +| | Perhaps it would make sense retiring this as it equals to | +| | return(0,0). It can be an optimisation by the EVM backend. | ++---------------------------------------------+-----------------------------------------------------------------+ | abort() | abort (equals to invalid instruction on EVM) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | selfdestruct(a:u256) | end execution, destroy current contract and send funds to a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) | | t3:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | | t3:u256, t4:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *State queries* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockcoinbase() -> address:u256 | current mining beneficiary | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockdifficulty() -> difficulty:u256 | difficulty of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockgaslimit() -> limit:u256 | block gas limit of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocknumber() -> block:u256 | current block number | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txorigin() -> address:u256 | transaction sender | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txgasprice() -> price:u256 | gas price of the transaction | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gasleft() -> gas:u256 | gas still available to execution | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | balance(a:u256) -> v:u256 | wei balance at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | this() -> address:u256 | address of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | caller() -> address:u256 | call sender (excluding delegatecall) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callvalue() -> v:u256 | wei sent together with the current call | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatasize() -> v:u256 | size of call data in bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codesize() -> size:u256 | size of the code of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodesize(a:u256) -> size:u256 | size of the code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Others* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | discardu256(unused:u256) | discard value | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's | | x3:u64, x4:u64) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 | | x4:u64) -> (x:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ Backends -------- From 59e6ea601b90577e39fe22a4c504f09e862dc40e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 22:14:17 +0000 Subject: [PATCH 045/242] Add bitwise shift operators to libevmasm (EIP145) --- libevmasm/Instruction.cpp | 10 ++++++++++ libevmasm/Instruction.h | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index b38981d27029..c25c9653591c 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -50,6 +50,11 @@ const std::map dev::solidity::c_instructions = { "OR", Instruction::OR }, { "XOR", Instruction::XOR }, { "BYTE", Instruction::BYTE }, + { "SHL", Instruction::SHL }, + { "SHR", Instruction::SHR }, + { "SAR", Instruction::SAR }, + { "ROL", Instruction::ROL }, + { "ROR", Instruction::ROR }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -190,6 +195,11 @@ static const std::map c_instructionInfo = { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ROL, { "ROL", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::ROR, { "ROR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index d9c53900b603..e56d4c9a1c66 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -59,8 +59,13 @@ enum class Instruction: uint8_t AND, ///< bitwise AND operation OR, ///< bitwise OR operation XOR, ///< bitwise XOR operation - NOT, ///< bitwise NOT opertation + NOT, ///< bitwise NOT operation BYTE, ///< retrieve single byte from word + SHL, ///< bitwise SHL operation + SHR, ///< bitwise SHR operation + SAR, ///< bitwise SAR operation + ROL, ///< bitwise ROL operation + ROR, ///< bitwise ROR operation KECCAK256 = 0x20, ///< compute KECCAK-256 hash From 6a83beaab52e0601c1e7654d27a8e72f53b77182 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 20:41:18 +0100 Subject: [PATCH 046/242] Run tests in parallel on circle. --- scripts/tests.sh | 100 ++++++++++++++++++++++++------------------- test/cmdlineTests.sh | 2 +- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index d414643b6c56..3c80adc5d507 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -45,54 +45,66 @@ else fi echo "Running commandline tests..." -"$REPO_ROOT/test/cmdlineTests.sh" +"$REPO_ROOT/test/cmdlineTests.sh" & +CMDLINE_PID=$! +# Only run in parallel if this is run on CI infrastructure +if [ -z "$CI" ] +then + wait $CMDLINE_PID +fi -# This conditional is only needed because we don't have a working Homebrew -# install for `eth` at the time of writing, so we unzip the ZIP file locally -# instead. This will go away soon. -if [[ "$OSTYPE" == "darwin"* ]]; then - ETH_PATH="$REPO_ROOT/eth" -elif [ -z $CI ]; then - ETH_PATH="eth" -else - mkdir -p /tmp/test - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" - if grep -i trusty /etc/lsb-release >/dev/null 2>&1 - then - ETH_BINARY=eth_byzantium2 - ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" +function download_eth() +{ + if [[ "$OSTYPE" == "darwin"* ]]; then + ETH_PATH="$REPO_ROOT/eth" + elif [ -z $CI ]; then + ETH_PATH="eth" + else + mkdir -p /tmp/test + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + if grep -i trusty /etc/lsb-release >/dev/null 2>&1 + then + ETH_BINARY=eth_byzantium2 + ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + fi + wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY + test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" + sync + chmod +x /tmp/test/eth + sync # Otherwise we might get a "text file busy" error + ETH_PATH="/tmp/test/eth" fi - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY - test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" - sync - chmod +x /tmp/test/eth - sync # Otherwise we might get a "text file busy" error - ETH_PATH="/tmp/test/eth" -fi -# This trailing ampersand directs the shell to run the command in the background, -# that is, it is forked and run in a separate sub-shell, as a job, -# asynchronously. The shell will immediately return the return status of 0 for -# true and continue as normal, either processing further commands in a script -# or returning the cursor focus back to the user in a Linux terminal. -$ETH_PATH --test -d /tmp/test & -ETH_PID=$! +} + +# $1: data directory +# echos the PID +function run_eth() +{ + $ETH_PATH --test -d "$1" >/dev/null 2>&1 & + echo $! + # Wait until the IPC endpoint is available. + while [ ! -S "$1"/geth.ipc ] ; do sleep 1; done + sleep 2 +} + +download_eth +ETH_PID=$(run_eth /tmp/test) + +progress="--show-progress" +if [ "$CI" ] +then + progress="" +fi -# Wait until the IPC endpoint is available. That won't be available instantly. -# The node needs to get a little way into its startup sequence before the IPC -# is available and is ready for the unit-tests to start talking to it. -while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done -echo "--> IPC available." -sleep 2 -# And then run the Solidity unit-tests (once without optimization, once with), -# pointing to that IPC endpoint. echo "--> Running tests without optimizer..." - "$REPO_ROOT"/build/test/soltest --show-progress $testargs_no_opt -- --ipcpath /tmp/test/geth.ipc && \ - echo "--> Running tests WITH optimizer..." && \ - "$REPO_ROOT"/build/test/soltest --show-progress $testargs_opt -- --optimize --ipcpath /tmp/test/geth.ipc -ERROR_CODE=$? +"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc +echo "--> Running tests WITH optimizer..." +"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc + +wait $CMDLINE_PID + pkill "$ETH_PID" || true sleep 4 -pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true -exit $ERROR_CODE +pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a249b601e731..32456fd0a7c1 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -172,4 +172,4 @@ TMPDIR=$(mktemp -d) done ) rm -rf "$TMPDIR" -echo "Done." +echo "Commandline tests successful." From 53289e15a2e1ea540a0c3abe28219c326a614fe5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 15:09:22 +0100 Subject: [PATCH 047/242] Make all lookups recursive by default. --- libsolidity/analysis/NameAndTypeResolver.cpp | 8 ++++---- libsolidity/analysis/NameAndTypeResolver.h | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 662792a346eb..2693eb974a1d 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -159,15 +159,15 @@ vector NameAndTypeResolver::resolveName(ASTString const& _na return iterator->second->resolveName(_name, false); } -vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const +vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name) const { - return m_currentScope->resolveName(_name, _recursive); + return m_currentScope->resolveName(_name, true); } -Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path, bool _recursive) const +Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path) const { solAssert(!_path.empty(), ""); - vector candidates = m_currentScope->resolveName(_path.front(), _recursive); + vector candidates = m_currentScope->resolveName(_path.front(), true); for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++) { if (!m_scopes.count(candidates.front())) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 9aea07abf187..35f5d31ee7f9 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -73,16 +73,17 @@ class NameAndTypeResolver: private boost::noncopyable /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the pre-defined global variables). /// @returns a pointer to the declaration on success or nullptr on failure. + /// SHOULD only be used for testing. std::vector resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const; - /// Resolves a name in the "current" scope. Should only be called during the initial - /// resolving phase. - std::vector nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const; + /// Resolves a name in the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. + std::vector nameFromCurrentScope(ASTString const& _name) const; - /// Resolves a path starting from the "current" scope. Should only be called during the initial - /// resolving phase. + /// Resolves a path starting from the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. /// @note Returns a null pointer if any component in the path was not unique or not found. - Declaration const* pathFromCurrentScope(std::vector const& _path, bool _recursive = true) const; + Declaration const* pathFromCurrentScope(std::vector const& _path) const; /// returns the vector of declarations without repetitions std::vector cleanedDeclarations( From 5f20129e65f5b8b714189145d177067152a21ac1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:53:25 +0100 Subject: [PATCH 048/242] Scopes do not have to be declarations. --- libsolidity/analysis/NameAndTypeResolver.cpp | 29 +++++++++++++------- libsolidity/analysis/NameAndTypeResolver.h | 5 +++- libsolidity/ast/AST.cpp | 11 ++++---- libsolidity/ast/AST.h | 26 ++++++++++++------ 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 2693eb974a1d..953b788ddc16 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -244,19 +244,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() } } +void NameAndTypeResolver::setScope(ASTNode const* _node) +{ + m_currentScope = m_scopes[_node].get(); +} + bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode) { if (ContractDefinition* contract = dynamic_cast(&_node)) { bool success = true; - m_currentScope = m_scopes[contract->scope()].get(); + setScope(contract->scope()); solAssert(!!m_currentScope, ""); for (ASTPointer const& baseContract: contract->baseContracts()) if (!resolveNamesAndTypes(*baseContract, true)) success = false; - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (success) { @@ -273,7 +278,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res // these can contain code, only resolve parameters for now for (ASTPointer const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, false)) { success = false; @@ -287,12 +292,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!_resolveInsideCode) return success; - m_currentScope = m_scopes[contract].get(); + setScope(contract); // now resolve references inside the code for (ASTPointer const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, true)) success = false; } @@ -301,7 +306,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res else { if (m_scopes.count(&_node)) - m_currentScope = m_scopes[&_node].get(); + setScope(&_node); return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); } } @@ -632,14 +637,17 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) closeCurrentScope(); } -void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) +void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { + if (auto s = dynamic_cast(&_subScope)) + s->setScope(m_currentScope); + map>::iterator iter; bool newlyAdded; shared_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); - tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container)); + tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); solAssert(newlyAdded, "Unable to add new scope."); - m_currentScope = &_declaration; + m_currentScope = &_subScope; } void DeclarationRegistrationHelper::closeCurrentScope() @@ -669,9 +677,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); - _declaration.setScope(m_currentScope); if (_opensScope) enterNewSubScope(_declaration); + else + _declaration.setScope(m_currentScope); } string DeclarationRegistrationHelper::currentCanonicalName() const diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 35f5d31ee7f9..d1fbe53c33a3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -97,6 +97,9 @@ class NameAndTypeResolver: private boost::noncopyable /// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions. std::string similarNameSuggestions(ASTString const& _name) const; + /// Sets the current scope. + void setScope(ASTNode const* _node); + private: /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors. bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true); @@ -169,7 +172,7 @@ class DeclarationRegistrationHelper: private ASTVisitor bool visit(EventDefinition& _event) override; void endVisit(EventDefinition& _event) override; - void enterNewSubScope(Declaration const& _declaration); + void enterNewSubScope(ASTNode& _subScope); void closeCurrentScope(); void registerDeclaration(Declaration& _declaration, bool _opensScope); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8da6964ef3d2..af007908f822 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -98,11 +98,12 @@ set SourceUnit::referencedSourceUnits(bool _recurse, set(scope) && dynamic_cast(scope)->m_scope) - scope = dynamic_cast(scope)->m_scope; - return dynamic_cast(*scope); + ASTNode const* s = scope(); + solAssert(s, ""); + // will not always be a declaratoion + while (dynamic_cast(s) && dynamic_cast(s)->scope()) + s = dynamic_cast(s)->scope(); + return dynamic_cast(*s); } string Declaration::sourceUnitName() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index c0d55aec45ec..a0089d64ab94 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -139,10 +139,26 @@ class SourceUnit: public ASTNode std::vector> m_nodes; }; +/** + * Abstract class that is added to each AST node that is stored inside a scope + * (including scopes). + */ +class Scopable +{ +public: + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. + /// Available only after name and type resolution step. + ASTNode const* scope() const { return m_scope; } + void setScope(ASTNode const* _scope) { m_scope = _scope; } + +protected: + ASTNode const* m_scope = nullptr; +}; + /** * Abstract AST class for a declaration (contract, function, struct, variable, import directive). */ -class Declaration: public ASTNode +class Declaration: public ASTNode, public Scopable { public: /// Visibility ordered from restricted to unrestricted. @@ -171,7 +187,7 @@ class Declaration: public ASTNode ASTPointer const& _name, Visibility _visibility = Visibility::Default ): - ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} + ASTNode(_location), m_name(_name), m_visibility(_visibility) {} /// @returns the declared name. ASTString const& name() const { return *m_name; } @@ -181,11 +197,6 @@ class Declaration: public ASTNode virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. - /// Available only after name and type resolution step. - ASTNode const* scope() const { return m_scope; } - void setScope(ASTNode const* _scope) { m_scope = _scope; } - /// @returns the source unit this declaration is present in. SourceUnit const& sourceUnit() const; @@ -213,7 +224,6 @@ class Declaration: public ASTNode private: ASTPointer m_name; Visibility m_visibility; - ASTNode const* m_scope; }; /** From e6d48bb72a82e2b3140d5a98da2961e401430a25 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:53:52 +0100 Subject: [PATCH 049/242] Blocks and for loops can be scopes. --- libsolidity/analysis/NameAndTypeResolver.cpp | 24 +++++++++++++++ libsolidity/analysis/NameAndTypeResolver.h | 4 +++ libsolidity/analysis/ReferencesResolver.cpp | 31 ++++++++++++++++++++ libsolidity/analysis/ReferencesResolver.h | 5 +++- libsolidity/ast/AST.cpp | 12 ++++++-- libsolidity/ast/AST.h | 8 +++-- 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 953b788ddc16..1566df943434 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -610,6 +610,30 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(Block& _block) +{ + enterNewSubScope(_block); + return true; +} + +void DeclarationRegistrationHelper::endVisit(Block&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(ForStatement& _for) +{ + // TODO special scoping rules for the init statement - if it is a block, then it should + // not open its own scope. + enterNewSubScope(_for); + return true; +} + +void DeclarationRegistrationHelper::endVisit(ForStatement&) +{ + closeCurrentScope(); +} + void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) { // Register the local variables with the function diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index d1fbe53c33a3..7a6e813e00a2 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -167,6 +167,10 @@ class DeclarationRegistrationHelper: private ASTVisitor void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; void endVisit(ModifierDefinition& _modifier) override; + bool visit(Block& _block) override; + void endVisit(Block& _block) override; + bool visit(ForStatement& _forLoop) override; + void endVisit(ForStatement& _forLoop) override; void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override; bool visit(VariableDeclaration& _declaration) override; bool visit(EventDefinition& _event) override; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 0bb5e3fef869..bee42e777494 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -43,6 +43,37 @@ bool ReferencesResolver::resolve(ASTNode const& _root) return !m_errorOccurred; } +bool ReferencesResolver::visit(Block const& _block) +{ + if (!m_resolveInsideCode) + return false; + m_resolver.setScope(&_block); + return true; +} + +void ReferencesResolver::endVisit(Block const& _block) +{ + if (!m_resolveInsideCode) + return; + + m_resolver.setScope(_block.scope()); +} + +bool ReferencesResolver::visit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return false; + m_resolver.setScope(&_for); + return true; +} + +void ReferencesResolver::endVisit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return; + m_resolver.setScope(_for.scope()); +} + bool ReferencesResolver::visit(Identifier const& _identifier) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index fef2e73f1f28..5bfd6c5a27a6 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -57,7 +57,10 @@ class ReferencesResolver: private ASTConstVisitor bool resolve(ASTNode const& _root); private: - virtual bool visit(Block const&) override { return m_resolveInsideCode; } + virtual bool visit(Block const& _block) override; + virtual void endVisit(Block const& _block) override; + virtual bool visit(ForStatement const& _for) override; + virtual void endVisit(ForStatement const& _for) override; virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(FunctionDefinition const& _functionDefinition) override; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index af007908f822..60a15aeb1aa0 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -415,6 +415,15 @@ bool VariableDeclaration::isLValue() const return !isExternalCallableParameter() && !m_isConstant; } +bool VariableDeclaration::isLocalVariable() const +{ + auto s = scope(); + return + dynamic_cast(s) || + dynamic_cast(s) || + dynamic_cast(s); +} + bool VariableDeclaration::isCallableParameter() const { auto const* callable = dynamic_cast(scope()); @@ -460,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const bool VariableDeclaration::canHaveAutoType() const { - auto const* callable = dynamic_cast(scope()); - return (!!callable && !isCallableParameter()); + return isLocalVariable() && !isCallableParameter(); } TypePointer VariableDeclaration::type() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a0089d64ab94..1e4c65913313 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -299,6 +299,8 @@ class ImportDirective: public Declaration /** * Abstract class that is added to each AST node that can store local variables. + * Local variables in functions are always added to functions, even though they are not + * in scope for the whole function. */ class VariableScope { @@ -672,7 +674,7 @@ class VariableDeclaration: public Declaration virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - bool isLocalVariable() const { return !!dynamic_cast(scope()); } + bool isLocalVariable() const; /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a return parameter of a function. @@ -1014,7 +1016,7 @@ class InlineAssembly: public Statement /** * Brace-enclosed block containing zero or more statements. */ -class Block: public Statement +class Block: public Statement, public Scopable { public: Block( @@ -1121,7 +1123,7 @@ class WhileStatement: public BreakableStatement /** * For loop statement */ -class ForStatement: public BreakableStatement +class ForStatement: public BreakableStatement, public Scopable { public: ForStatement( From 69f5f2eb65969c8aa1ee0438601c0a0938f2d0a5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:54:08 +0100 Subject: [PATCH 050/242] Adjust tests. --- test/libsolidity/SMTChecker.cpp | 6 ++++-- .../SolidityExpressionCompiler.cpp | 19 +++++++++++-------- .../SolidityNameAndTypeResolution.cpp | 12 ++++++++---- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 5088ab9473d6..12b5f439617a 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -466,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 3); @@ -477,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 2); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 677473866374..e2a0c3cdb3ad 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = R"( contract test { - function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } + function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x3, @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::PUSH1), 0x6, byte(Instruction::PUSH1), 0x7, byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP10), + byte(Instruction::DUP9), byte(Instruction::XOR), byte(Instruction::AND), byte(Instruction::OR), @@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = R"( contract test { - function f(int y) { var x = !(~+- y == 2); } + function f(int y) { !(~+- y == 2); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP3), + byte(Instruction::DUP2), byte(Instruction::PUSH1), 0x0, byte(Instruction::SUB), byte(Instruction::NOT), @@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) byte(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) byte(Instruction::DUP3), // will change - byte(Instruction::XOR)}); + byte(Instruction::XOR), + byte(Instruction::SWAP1), + byte(Instruction::POP), + byte(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be147e48f0a2..a8adde1e6d6d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -76,15 +76,19 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration) { - char const* text = R"( + string text = R"( contract test { - function f() public { + function f() pure public { uint256 x; if (true) { uint256 x; } } } )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "This declaration shadows an existing declaration.", + "Unused local variable", + "Unused local variable" + })); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -1043,7 +1047,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) modifier mod(uint a) { if (a > 0) _; } } )"; - CHECK_SUCCESS(text); + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) From 000599038202154afe62b144a44dd81178fc539c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Feb 2018 16:54:13 +0100 Subject: [PATCH 051/242] Tests for new scoping behaviour. --- .../SolidityNameAndTypeResolution.cpp | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a8adde1e6d6d..be0426552648 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -91,6 +91,50 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) })); } +BOOST_AUTO_TEST_CASE(scoping) +{ + char const* text = R"( + contract test { + function f() public { + { + uint256 x; + } + x = 2; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for) +{ + char const* text = R"( + contract test { + function f() public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_for2) +{ + char const* text = R"( + contract test { + function f() public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + BOOST_AUTO_TEST_CASE(name_shadowing) { char const* text = R"( From e227bdbfa7157d2675c177c75d44e13f0bd82214 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 08:11:36 +0100 Subject: [PATCH 052/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index acd3aa54195b..db4f97863151 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.21 (unreleased) Features: + * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. From 6b9dda06f3d85344a70efb2f868760a0dde9dc45 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 14 Feb 2018 01:48:40 +0100 Subject: [PATCH 053/242] Enable C99-scoping with the 0.5.0-experimental pragma. --- libsolidity/analysis/NameAndTypeResolver.cpp | 30 +++++++----- libsolidity/analysis/ReferencesResolver.cpp | 17 +++++-- libsolidity/analysis/ReferencesResolver.h | 1 + libsolidity/ast/AST.cpp | 30 ++++++------ libsolidity/ast/AST.h | 13 ++--- .../SolidityNameAndTypeResolution.cpp | 47 ++++++++++++++++++- 6 files changed, 98 insertions(+), 40 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 1566df943434..40021771b1af 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -612,26 +612,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) bool DeclarationRegistrationHelper::visit(Block& _block) { - enterNewSubScope(_block); + _block.setScope(m_currentScope); + // Enable C99-scoped variables. + if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + enterNewSubScope(_block); return true; } -void DeclarationRegistrationHelper::endVisit(Block&) +void DeclarationRegistrationHelper::endVisit(Block& _block) { - closeCurrentScope(); + // Enable C99-scoped variables. + if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ForStatement& _for) { - // TODO special scoping rules for the init statement - if it is a block, then it should - // not open its own scope. - enterNewSubScope(_for); + _for.setScope(m_currentScope); + if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + // TODO special scoping rules for the init statement - if it is a block, then it should + // not open its own scope. + enterNewSubScope(_for); return true; } -void DeclarationRegistrationHelper::endVisit(ForStatement&) +void DeclarationRegistrationHelper::endVisit(ForStatement& _for) { - closeCurrentScope(); + if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + closeCurrentScope(); } void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) @@ -663,9 +671,6 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { - if (auto s = dynamic_cast(&_subScope)) - s->setScope(m_currentScope); - map>::iterator iter; bool newlyAdded; shared_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); @@ -701,10 +706,9 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); + _declaration.setScope(m_currentScope); if (_opensScope) enterNewSubScope(_declaration); - else - _declaration.setScope(m_currentScope); } string DeclarationRegistrationHelper::currentCanonicalName() const diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index bee42e777494..4d919f2bd737 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -47,7 +47,10 @@ bool ReferencesResolver::visit(Block const& _block) { if (!m_resolveInsideCode) return false; - m_resolver.setScope(&_block); + m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_block); return true; } @@ -56,14 +59,19 @@ void ReferencesResolver::endVisit(Block const& _block) if (!m_resolveInsideCode) return; - m_resolver.setScope(_block.scope()); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(_block.scope()); } bool ReferencesResolver::visit(ForStatement const& _for) { if (!m_resolveInsideCode) return false; - m_resolver.setScope(&_for); + m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_for); return true; } @@ -71,7 +79,8 @@ void ReferencesResolver::endVisit(ForStatement const& _for) { if (!m_resolveInsideCode) return; - m_resolver.setScope(_for.scope()); + if (m_experimental050Mode) + m_resolver.setScope(_for.scope()); } bool ReferencesResolver::visit(Identifier const& _identifier) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 5bfd6c5a27a6..ab7c987e0ca9 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -93,6 +93,7 @@ class ReferencesResolver: private ASTConstVisitor std::vector m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; + bool m_experimental050Mode = false; }; } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 60a15aeb1aa0..27220b1f3723 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -96,21 +96,6 @@ set SourceUnit::referencedSourceUnits(bool _recurse, set(s) && dynamic_cast(s)->scope()) - s = dynamic_cast(s)->scope(); - return dynamic_cast(*s); -} - -string Declaration::sourceUnitName() const -{ - return sourceUnit().annotation().path; -} - ImportAnnotation& ImportDirective::annotation() const { if (!m_annotation) @@ -409,6 +394,21 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const return dynamic_cast(*m_annotation); } +SourceUnit const& Scopable::sourceUnit() const +{ + ASTNode const* s = scope(); + solAssert(s, ""); + // will not always be a declaratoion + while (dynamic_cast(s) && dynamic_cast(s)->scope()) + s = dynamic_cast(s)->scope(); + return dynamic_cast(*s); +} + +string Scopable::sourceUnitName() const +{ + return sourceUnit().annotation().path; +} + bool VariableDeclaration::isLValue() const { // External function parameters and constant declared variables are Read-Only diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 1e4c65913313..863ad2fecb53 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -151,6 +151,13 @@ class Scopable ASTNode const* scope() const { return m_scope; } void setScope(ASTNode const* _scope) { m_scope = _scope; } + /// @returns the source unit this scopable is present in. + SourceUnit const& sourceUnit() const; + + /// @returns the source name this scopable is present in. + /// Can be combined with annotation().canonicalName (if present) to form a globally unique name. + std::string sourceUnitName() const; + protected: ASTNode const* m_scope = nullptr; }; @@ -197,12 +204,6 @@ class Declaration: public ASTNode, public Scopable virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - /// @returns the source unit this declaration is present in. - SourceUnit const& sourceUnit() const; - - /// @returns the source name this declaration is present in. - /// Can be combined with annotation().canonicalName to form a globally unique name. - std::string sourceUnitName() const; std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be0426552648..8586ebf848e3 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -84,16 +84,45 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) } } )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } + } + )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ "This declaration shadows an existing declaration.", + "Experimental features", "Unused local variable", "Unused local variable" })); } +BOOST_AUTO_TEST_CASE(scoping_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(scoping) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { { @@ -109,6 +138,7 @@ BOOST_AUTO_TEST_CASE(scoping) BOOST_AUTO_TEST_CASE(scoping_for) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { for (uint x = 0; x < 10; x ++){ @@ -123,6 +153,7 @@ BOOST_AUTO_TEST_CASE(scoping_for) BOOST_AUTO_TEST_CASE(scoping_for2) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { for (uint x = 0; x < 10; x ++){ @@ -1052,7 +1083,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( contract B { - function f() mod1(2, true) mod2("0123456") public { } + function f() mod1(2, true) mod2("0123456") pure public { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -1087,7 +1118,19 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) { char const* text = R"( contract B { - function f() mod(x) public { uint x = 7; } + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract B { + function f() mod(x) pure public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } )"; From 88a5c66f4a7efae6d78ce2e04144219656bb9da0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 11:30:32 +0100 Subject: [PATCH 054/242] Only active variables at the point of their declaration. --- libsolidity/analysis/DeclarationContainer.cpp | 33 ++++++-- libsolidity/analysis/DeclarationContainer.h | 6 +- libsolidity/analysis/NameAndTypeResolver.cpp | 54 ++++++++----- libsolidity/analysis/NameAndTypeResolver.h | 10 ++- libsolidity/analysis/ReferencesResolver.cpp | 10 +++ libsolidity/analysis/ReferencesResolver.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 20 +++++ .../SolidityNameAndTypeResolution.cpp | 75 ++++++++++++++++++- 8 files changed, 176 insertions(+), 33 deletions(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7508ad9e1ddd..c7ba78d60947 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return nullptr; } +void DeclarationContainer::activateVariable(ASTString const& _name) +{ + solAssert( + m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1, + "Tried to activate a non-inactive variable or multiple inactive variables with the same name." + ); + solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), ""); + m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front()); + m_invisibleDeclarations.erase(_name); +} + bool DeclarationContainer::registerDeclaration( Declaration const& _declaration, ASTString const* _name, @@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration( return true; } -vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); - auto result = m_declarations.find(_name); - if (result != m_declarations.end()) - return result->second; - if (_recursive && m_enclosingContainer) - return m_enclosingContainer->resolveName(_name, true); - return vector({}); + vector result; + if (m_declarations.count(_name)) + result = m_declarations.at(_name); + if (_alsoInvisible && m_invisibleDeclarations.count(_name)) + result += m_invisibleDeclarations.at(_name); + if (result.empty() && _recursive && m_enclosingContainer) + result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible); + return result; } vector DeclarationContainer::similarNames(ASTString const& _name) const @@ -129,6 +142,12 @@ vector DeclarationContainer::similarNames(ASTString const& _name) con if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) similar.push_back(declarationName); } + for (auto const& declaration: m_invisibleDeclarations) + { + string const& declarationName = declaration.first; + if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + similar.push_back(declarationName); + } if (m_enclosingContainer) similar += m_enclosingContainer->similarNames(_name); diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index f9b1bda4b003..e4b3320ae252 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -51,13 +51,17 @@ class DeclarationContainer /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); - std::vector resolveName(ASTString const& _name, bool _recursive = false) const; + std::vector resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const; ASTNode const* enclosingNode() const { return m_enclosingNode; } DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; } std::map> const& declarations() const { return m_declarations; } /// @returns whether declaration is valid, and if not also returns previous declaration. Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(ASTString const& _name); + /// @returns existing declaration names similar to @a _name. /// Searches this and all parent containers. std::vector similarNames(ASTString const& _name) const; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 40021771b1af..2f6751351aef 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver( m_scopes[nullptr]->registerDeclaration(*declaration); } -bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope) +bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) { + bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050); // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope); } catch (FatalError const&) { @@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), true, m_errorReporter + target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter )) error = true; } @@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapsecond->declarations()) for (auto const& declaration: nameAndDeclaration.second) if (!DeclarationRegistrationHelper::registerDeclaration( - target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter + target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter )) error = true; } @@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) return true; } +void NameAndTypeResolver::activateVariable(string const& _name) +{ + solAssert(m_currentScope, ""); + m_currentScope->activateVariable(_name); +} + vector NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const { auto iterator = m_scopes.find(_scope); @@ -159,9 +166,9 @@ vector NameAndTypeResolver::resolveName(ASTString const& _na return iterator->second->resolveName(_name, false); } -vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name) const +vector NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const { - return m_currentScope->resolveName(_name, true); + return m_currentScope->resolveName(_name, true, _includeInvisibles); } Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector const& _path) const @@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() for (auto const& instruction: c_instructions) { string const instructionName{boost::algorithm::to_lower_copy(instruction.first)}; - auto declarations = nameFromCurrentScope(instructionName); + auto declarations = nameFromCurrentScope(instructionName, true); for (Declaration const* const declaration: declarations) { solAssert(!!declaration, ""); @@ -439,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const DeclarationRegistrationHelper::DeclarationRegistrationHelper( map>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope ): + m_useC99Scoping(_useC99Scoping), m_scopes(_scopes), m_currentScope(_currentScope), m_errorReporter(_errorReporter) @@ -456,6 +465,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ) { @@ -465,10 +475,13 @@ bool DeclarationRegistrationHelper::registerDeclaration( string name = _name ? *_name : _declaration.name(); Declaration const* shadowedDeclaration = nullptr; if (_warnOnShadow && !name.empty() && _container.enclosingContainer()) - for (auto const* decl: _container.enclosingContainer()->resolveName(name, true)) + for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true)) shadowedDeclaration = decl; - if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract())) + // We use "invisible" for both inactive variables in blocks and for members invisible in contracts. + // They cannot both be true at the same time. + solAssert(!(_inactive && !_declaration.isVisibleInContract()), ""); + if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive)) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; @@ -613,32 +626,28 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) bool DeclarationRegistrationHelper::visit(Block& _block) { _block.setScope(m_currentScope); - // Enable C99-scoped variables. - if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) enterNewSubScope(_block); return true; } -void DeclarationRegistrationHelper::endVisit(Block& _block) +void DeclarationRegistrationHelper::endVisit(Block&) { - // Enable C99-scoped variables. - if (_block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ForStatement& _for) { _for.setScope(m_currentScope); - if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - // TODO special scoping rules for the init statement - if it is a block, then it should - // not open its own scope. + if (m_useC99Scoping) enterNewSubScope(_for); return true; } -void DeclarationRegistrationHelper::endVisit(ForStatement& _for) +void DeclarationRegistrationHelper::endVisit(ForStatement&) { - if (_for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (m_useC99Scoping) closeCurrentScope(); } @@ -704,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio if (fun->isConstructor()) warnAboutShadowing = false; - registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); + // Register declaration as inactive if we are in block scope and C99 mode. + bool inactive = + m_useC99Scoping && + (dynamic_cast(m_currentScope) || dynamic_cast(m_currentScope)); + + registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); _declaration.setScope(m_currentScope); if (_opensScope) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 7a6e813e00a2..3d10fbd87e36 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -56,7 +56,7 @@ class NameAndTypeResolver: private boost::noncopyable /// @returns false in case of error. /// @param _currentScope should be nullptr but can be used to inject new declarations into /// existing scopes, used by the snippets feature. - bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr); + bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr); /// Applies the effect of import directives. bool performImports(SourceUnit& _sourceUnit, std::map const& _sourceUnits); /// Resolves all names and types referenced from the given AST Node. @@ -69,6 +69,9 @@ class NameAndTypeResolver: private boost::noncopyable /// that create their own scope. /// @returns false in case of error. bool updateDeclaration(Declaration const& _declaration); + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(std::string const& _name); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the pre-defined global variables). @@ -78,7 +81,7 @@ class NameAndTypeResolver: private boost::noncopyable /// Resolves a name in the "current" scope, but also searches parent scopes. /// Should only be called during the initial resolving phase. - std::vector nameFromCurrentScope(ASTString const& _name) const; + std::vector nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const; /// Resolves a path starting from the "current" scope, but also searches parent scopes. /// Should only be called during the initial resolving phase. @@ -139,6 +142,7 @@ class DeclarationRegistrationHelper: private ASTVisitor DeclarationRegistrationHelper( std::map>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -149,6 +153,7 @@ class DeclarationRegistrationHelper: private ASTVisitor std::string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ); @@ -185,6 +190,7 @@ class DeclarationRegistrationHelper: private ASTVisitor /// @returns the canonical name of the current scope. std::string currentCanonicalName() const; + bool m_useC99Scoping = false; std::map>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4d919f2bd737..985c44d0ab00 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -83,6 +83,16 @@ void ReferencesResolver::endVisit(ForStatement const& _for) m_resolver.setScope(_for.scope()); } +void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) +{ + if (!m_resolveInsideCode) + return; + if (m_experimental050Mode) + for (auto const& var: _varDeclStatement.declarations()) + if (var) + m_resolver.activateVariable(var->name()); +} + bool ReferencesResolver::visit(Identifier const& _identifier) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index ab7c987e0ca9..4e8f54b5e4e6 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -61,6 +61,7 @@ class ReferencesResolver: private ASTConstVisitor virtual void endVisit(Block const& _block) override; virtual bool visit(ForStatement const& _for) override; virtual void endVisit(ForStatement const& _for) override; + virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(FunctionDefinition const& _functionDefinition) override; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3882e4ea6990..7bae3cd632ec 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -284,6 +284,26 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(C99_scoping_activation) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public returns (uint) { + uint x = 7; + { + x = 3; // This should still assign to the outer variable + uint x; + x = 4; // This should assign to the new one + } + return x; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8586ebf848e3..e5bd0103f721 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -135,19 +135,59 @@ BOOST_AUTO_TEST_CASE(scoping) CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); } -BOOST_AUTO_TEST_CASE(scoping_for) +BOOST_AUTO_TEST_CASE(scoping_activation_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_activation) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use) { char const* text = R"( pragma experimental "v0.5.0"; contract test { function f() public { + uint a = a; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { for (uint x = 0; x < 10; x ++){ x = 2; } } } )"; - CHECK_SUCCESS(text); + CHECK_WARNING(text, "Experimental features"); } BOOST_AUTO_TEST_CASE(scoping_for2) @@ -155,7 +195,21 @@ BOOST_AUTO_TEST_CASE(scoping_for2) char const* text = R"( pragma experimental "v0.5.0"; contract test { - function f() public { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(scoping_for3) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { for (uint x = 0; x < 10; x ++){ x = 2; } @@ -166,6 +220,21 @@ BOOST_AUTO_TEST_CASE(scoping_for2) CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); } +BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + BOOST_AUTO_TEST_CASE(name_shadowing) { char const* text = R"( From 6391a36a6c71c1e8177358bc3832e32504e73111 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 11:43:53 +0100 Subject: [PATCH 055/242] Documentation about new scoping rules. --- docs/control-structures.rst | 61 +++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7be92cfa0008..46e076e53458 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes`` and ``string``, the default value is an empty array or string. -A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared. +A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared +(this will change soon, see below). This happens because Solidity inherits its scoping rules from JavaScript. This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block. As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:: @@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er } In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value. -As a result, the following code is legal, despite being poorly written:: +As a result, the following code is legal, despite being poorly written: + +:: pragma solidity ^0.4.0; @@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written:: } } +Scoping starting from Version 0.5.0 +----------------------------------- + +Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99 +(and many other languages): Variables are visible from the point right after their declaration +until the end of a ``{ }``-block. As an exception to this rule, variables declared in the +initialization part of a for-loop are only visible until the end of the for-loop. + +Variables and other items declared outside of a code block, for example functions, contracts, +user-defined types, etc., do not change their scoping behaviour. This means you can +use state variables before they are declared and call functions recursively. + +These rules are already introduced now as an experimental feature. + +As a consequence, the following examples will compile without warnings, since +the two variables have the same name but disjoint scopes. In non-0.5.0-mode, +they have the same scope (the function ``minimalScoping``) and thus it does +not compile there. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function minimalScoping() pure public { + { + uint same2 = 0; + } + + { + uint same2 = 0; + } + } + } + +As a special example of the C99 scoping rules, note that in the following, +the first assignment to ``x`` will actually assign the outer and not the inner variable. +In any case, you will get a warning about the outer variable being shadowed. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function f() pure public returns (uint) { + uint x = 1; + { + x = 2; // this will assign to the outer variable + uint x; + } + return x; // x has value 2 + } + } + .. index:: ! exception, ! throw, ! assert, ! require, ! revert Error handling: Assert, Require, Revert and Exceptions From d64aa0eaad50741896020d31c0c93b64c3f03bc1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 21:14:20 +0100 Subject: [PATCH 056/242] Some more scoping tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 28 +++++++ .../SolidityNameAndTypeResolution.cpp | 75 ++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7bae3cd632ec..c352a2c22518 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -298,10 +298,38 @@ BOOST_AUTO_TEST_CASE(C99_scoping_activation) } return x; } + function g() pure public returns (uint x) { + x = 7; + { + x = 3; + uint x; + return x; // This returns the new variable, i.e. 0 + } + } + function h() pure public returns (uint x, uint a, uint b) { + x = 7; + { + x = 3; + a = x; // This should read from the outer + uint x = 4; + b = x; + } + } + function i() pure public returns (uint x, uint a) { + x = 7; + { + x = 3; + uint x = x; // This should read from the outer and assign to the inner + a = x; + } + } } )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3)); } BOOST_AUTO_TEST_CASE(recursive_calls) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5bd0103f721..419e46a7d226 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -106,6 +106,67 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050) })); } +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} BOOST_AUTO_TEST_CASE(scoping_old) { char const* text = R"( @@ -163,11 +224,23 @@ BOOST_AUTO_TEST_CASE(scoping_activation) } BOOST_AUTO_TEST_CASE(scoping_self_use) +{ + char const* text = R"( + contract test { + function f() pure public { + uint a = a; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use_050) { char const* text = R"( pragma experimental "v0.5.0"; contract test { - function f() public { + function f() pure public { uint a = a; } } From 9e0446a22c78e8a293a742492f2d2d7cd67af4bd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 6 Feb 2017 22:42:27 +0000 Subject: [PATCH 057/242] Document bitwise shift operators in assembly --- docs/assembly.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index 46416142776d..245b538327b7 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -206,6 +206,16 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | +-------------------------+-----+---+-----------------------------------------------------------------+ +| shl(x, y) | | C | logical shift left x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| shr(x, y) | | C | logical shift right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sar(x, y) | | C | arithmetic shift right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| ror(x, y) | | C | rotate right x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| rol(x, y) | | C | rotate left x by y bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ | mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | From 468d0f6199e71f0c7f4b8bd667c8f31feba41a9d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 15 Jun 2017 12:14:14 +0100 Subject: [PATCH 058/242] Warn on using shift instructions --- libsolidity/inlineasm/AsmAnalysis.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index a05ac57d5900..7653ee511b42 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -548,6 +548,22 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); + static set experimentalInstructions{ + solidity::Instruction::SHL, + solidity::Instruction::SHR, + solidity::Instruction::SAR, + solidity::Instruction::ROL, + solidity::Instruction::ROR + }; + if (experimentalInstructions.count(_instr)) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available after " + + "the Constantinople hard fork. Before that it acts as an invalid instruction." + ); + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, From afa4a48e3770630a744ef98ccd518601e1f35c86 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 9 Jul 2017 08:42:54 +0100 Subject: [PATCH 059/242] Remove ROL/ROR as they are not part of EIP145 anymore --- docs/assembly.rst | 4 ---- libevmasm/Instruction.cpp | 4 ---- libevmasm/Instruction.h | 2 -- libsolidity/inlineasm/AsmAnalysis.cpp | 4 +--- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 245b538327b7..9eabc99ee4ee 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -212,10 +212,6 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | sar(x, y) | | C | arithmetic shift right x by y bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| ror(x, y) | | C | rotate right x by y bits | -+-------------------------+-----+---+-----------------------------------------------------------------+ -| rol(x, y) | | C | rotate left x by y bits | -+-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ | mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index c25c9653591c..a677a631cf61 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -53,8 +53,6 @@ const std::map dev::solidity::c_instructions = { "SHL", Instruction::SHL }, { "SHR", Instruction::SHR }, { "SAR", Instruction::SAR }, - { "ROL", Instruction::ROL }, - { "ROR", Instruction::ROR }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -198,8 +196,6 @@ static const std::map c_instructionInfo = { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } }, - { Instruction::ROL, { "ROL", 0, 2, 1, false, Tier::VeryLow } }, - { Instruction::ROR, { "ROR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index e56d4c9a1c66..be788ddb422b 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -64,8 +64,6 @@ enum class Instruction: uint8_t SHL, ///< bitwise SHL operation SHR, ///< bitwise SHR operation SAR, ///< bitwise SAR operation - ROL, ///< bitwise ROL operation - ROR, ///< bitwise ROR operation KECCAK256 = 0x20, ///< compute KECCAK-256 hash diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 7653ee511b42..1030523a6068 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -551,9 +551,7 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio static set experimentalInstructions{ solidity::Instruction::SHL, solidity::Instruction::SHR, - solidity::Instruction::SAR, - solidity::Instruction::ROL, - solidity::Instruction::ROR + solidity::Instruction::SAR }; if (experimentalInstructions.count(_instr)) m_errorReporter.warning( From 317e017849e3ada43842c1e0268338e54c0c4d8e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 Jan 2018 00:29:14 +0000 Subject: [PATCH 060/242] Shift operands were swapped in accepted EIP145 --- docs/assembly.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 9eabc99ee4ee..cf9bf840fde0 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -206,11 +206,11 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | +-------------------------+-----+---+-----------------------------------------------------------------+ -| shl(x, y) | | C | logical shift left x by y bits | +| shl(x, y) | | C | logical shift left y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| shr(x, y) | | C | logical shift right x by y bits | +| shr(x, y) | | C | logical shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| sar(x, y) | | C | arithmetic shift right x by y bits | +| sar(x, y) | | C | arithmetic shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +-------------------------+-----+---+-----------------------------------------------------------------+ From 73c5d99bfaa4d17a97d4554a89d1005b25e62f98 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 12:09:22 +0100 Subject: [PATCH 061/242] Add basic test for shift opcodes --- test/libsolidity/InlineAssembly.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 45fb54f80d01..ea120657ad03 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -774,6 +774,20 @@ BOOST_AUTO_TEST_CASE(create2) BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); } +BOOST_AUTO_TEST_CASE(shift) +{ + BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }")); +} + +BOOST_AUTO_TEST_CASE(shift_constantinople_warning) +{ + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork"); +} + BOOST_AUTO_TEST_CASE(jump_warning) { CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); From 9a8b0d5726a4402135151dce9208dc4f131713c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 Sep 2017 12:53:31 +0200 Subject: [PATCH 062/242] Fix install_deps.sh for Debian without lsb_release --- scripts/install_deps.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index d62cffb70657..e884ed652725 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -168,11 +168,12 @@ case $(uname -s) in # Debian #------------------------------------------------------------------------------ - Debian) + Debian*) #Debian + . /etc/os-release install_z3="" - case $(lsb_release -cs) in - wheezy) + case $VERSION_ID in + 7) #wheezy echo "Installing solidity dependencies on Debian Wheezy (7.x)." echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." @@ -182,16 +183,16 @@ case $(uname -s) in echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." exit 1 ;; - jessie) + 8) #jessie echo "Installing solidity dependencies on Debian Jesse (8.x)." ;; - stretch) + 9) #stretch echo "Installing solidity dependencies on Debian Stretch (9.x)." install_z3="libz3-dev" ;; - buster) + 10) #buster echo "Installing solidity dependencies on Debian Buster (10.x)." install_z3="libz3-dev" From a566825589a2c0d41e2e136527b98737cffb5701 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 15:21:23 +0100 Subject: [PATCH 063/242] Issue error if no visibility is specified (on 0.5.0) --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 19 ++++++++---- .../SolidityNameAndTypeResolution.cpp | 29 +++++++++++++++---- test/libsolidity/ViewPureChecker.cpp | 2 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Changelog.md b/Changelog.md index acd3aa54195b..fdc0814ab0ed 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. + * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 1dcfeb27142a..ddac194b9b1d 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -212,13 +212,20 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(FunctionDefinition const& _function) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_function.noVisibilitySpecified()) - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\"." - ); + { + if (v050) + m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + else + m_errorReporter.warning( + _function.location(), + "No visibility specified. Defaulting to \"" + + Declaration::visibilityToString(_function.visibility()) + + "\"." + ); + } return true; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index be147e48f0a2..be7d09ef98e6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7741,7 +7741,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) char const* text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.balance; } } @@ -7750,7 +7750,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.transfer; } } @@ -7759,7 +7759,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.send; } } @@ -7768,7 +7768,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.call; } } @@ -7777,7 +7777,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.callcode; } } @@ -7786,7 +7786,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.delegatecall; } } @@ -7870,6 +7870,23 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type) } } +BOOST_AUTO_TEST_CASE(require_visibility_specifiers) +{ + char const* text = R"( + contract C { + function f() pure { } + } + )"; + CHECK_WARNING(text, "No visibility specified. Defaulting to"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure { } + } + )"; + CHECK_ERROR(text, SyntaxError, "No visibility specified."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 3a03c877228a..2599ca28affd 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(environment_access) BOOST_AUTO_TEST_CASE(view_error_for_050) { CHECK_ERROR( - "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }", + "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }", TypeError, "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); From 8899812ff2f5c0323001c792e94b98788ca4c6e6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 16:38:16 +0100 Subject: [PATCH 064/242] Disable tests for travis on non-release and non-tag branches. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0b05f6610ac5..ed4584ef587b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -159,6 +159,7 @@ cache: install: - test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh) - test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh) + - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" ]; then SOLC_TESTS=Off; fi - if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - echo -n "$TRAVIS_COMMIT" > commit_hash.txt From 029e19983cdc444749258d21b448e52c48d2088d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 19:28:48 +0100 Subject: [PATCH 065/242] Split compileContract off compileAndRunWithoutCheck in SolidityExecutionFramework --- test/libsolidity/SolidityExecutionFramework.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index b853d558789c..f562721d3dea 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -51,6 +51,17 @@ class SolidityExecutionFramework: public dev::test::ExecutionFramework bytes const& _arguments = bytes(), std::map const& _libraryAddresses = std::map() ) override + { + bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses); + sendMessage(bytecode + _arguments, true, _value); + return m_output; + } + + bytes compileContract( + std::string const& _sourceCode, + std::string const& _contractName = "", + std::map const& _libraryAddresses = std::map() + ) { // Silence compiler version warning std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode; @@ -72,8 +83,7 @@ class SolidityExecutionFramework: public dev::test::ExecutionFramework } eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); - sendMessage(obj.bytecode + _arguments, true, _value); - return m_output; + return obj.bytecode; } protected: From 0346f723422698158b4d5d9b4d7737d185cd8e52 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 19:32:05 +0100 Subject: [PATCH 066/242] Use compileContract in contract tests --- test/contracts/AuctionRegistrar.cpp | 9 ++------- test/contracts/FixedFeeRegistrar.cpp | 9 ++------- test/contracts/Wallet.cpp | 9 ++------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index c9c744af5a33..5e4991e2ca2a 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -220,13 +220,8 @@ class AuctionRegistrarTestFramework: public SolidityExecutionFramework void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 8327999daad9..a3a27c37702b 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -132,13 +132,8 @@ class RegistrarTestFramework: public SolidityExecutionFramework void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 90334ad61c56..1031e8f1902c 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -447,13 +447,8 @@ class WalletTestFramework: public SolidityExecutionFramework ) { if (!s_compiledWallet) - { - m_compiler.reset(false); - m_compiler.addSource("", walletCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); - } + s_compiledWallet.reset(new bytes(compileContract(walletCode, "Wallet"))); + bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); BOOST_REQUIRE(!m_output.empty()); From 5fd9ea4b2abf9d2498fa725cfdbdf152c3d3d15a Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Tue, 27 Feb 2018 12:31:09 +0800 Subject: [PATCH 067/242] Stop allow_guessing for command line option parser --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 1 + test/cmdlineTests.sh | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3af3a99d9c5d..8aa7ba0cc67c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Bugfixes: * Standalone Assembly: Do not ignore input after closing brace of top level block. * Standard JSON: catch errors properly when invalid "sources" are passed * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. + * Commandline interface: throw error if option is unknown ### 0.4.20 (2018-02-14) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8383afed96a1..62b24975561a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -627,6 +627,7 @@ Allowed options)", try { po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); cmdLineParser.options(allOptions).positional(filesPositions); po::store(cmdLineParser.run(), m_args); } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a249b601e731..f2c1f2c3a797 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -83,6 +83,21 @@ function compileWithoutWarning() test -z "$output" -a "$failed" -eq 0 } +printTask "Testing unknown options..." +( + set +e + output=$("$SOLC" --allow=test 2>&1) + failed=$? + set -e + + if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] ; then + echo "Passed" + else + printError "Incorrect response to unknown options: $STDERR" + exit 1 + fi +) + printTask "Compiling various other contracts and libraries..." ( cd "$REPO_ROOT"/test/compilationTests/ From 4f34d092ca84f71cb477129ffeeb471dba6bf772 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Wed, 28 Feb 2018 09:35:07 +0100 Subject: [PATCH 068/242] Update miscellaneous.rst --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 328ec6eaa0f2..b5d605ac33ce 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -219,7 +219,7 @@ This means the following source mappings represent the same information: ``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` -``1:2:1;:9;2::2;;`` +``1:2:1;:9;2:1:2;;`` *************** Tips and Tricks From 83fec0232d38eb6214eb41104b6cd51b6f21f282 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 10:36:07 +0100 Subject: [PATCH 069/242] Add more comprehensive tests and checks for libraries in JSONIO --- libsolidity/interface/StandardCompiler.cpp | 6 +- test/libsolidity/StandardCompiler.cpp | 110 +++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index fb973d518ef5..6b1136545b23 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -327,10 +327,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); map libraries; - Json::Value jsonLibraries = settings.get("libraries", Json::Value()); + Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); + if (!jsonLibraries.isObject()) + return formatFatalError("JSONError", "\"libraries\" is not a JSON object."); for (auto const& sourceName: jsonLibraries.getMemberNames()) { auto const& jsonSourceName = jsonLibraries[sourceName]; + if (!jsonSourceName.isObject()) + return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) // @TODO use libraries only for the given source libraries[library] = h160(jsonSourceName[library].asString()); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 404f709d964e..c4caf20391fb 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -570,6 +570,116 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); } +BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": "42" + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_entry) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "L": "42" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_various_addresses) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": 42, + "L3": "42", + "L4": "0x42", + "L5": "0x4200000000000000000000000000000000000001", + "L6": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); +} + +BOOST_AUTO_TEST_CASE(library_linking) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }" + }, + "library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + }, + "library2.sol": { + "content": "library L2 { function g() { } }" + } + } + } + )"; + 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"]["library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); +} BOOST_AUTO_TEST_SUITE_END() From 7897301b7179603a1bc74d7be9eff6ccc67398db Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 10:44:48 +0100 Subject: [PATCH 070/242] Properly validate invalid hex characters in JSONIO libraries --- libsolidity/interface/StandardCompiler.cpp | 16 +++++++++++++-- test/libsolidity/StandardCompiler.cpp | 23 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 6b1136545b23..8c64c16462de 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -336,8 +336,20 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (!jsonSourceName.isObject()) return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) - // @TODO use libraries only for the given source - libraries[library] = h160(jsonSourceName[library].asString()); + { + try + { + // @TODO use libraries only for the given source + libraries[library] = h160(jsonSourceName[library].asString()); + } + catch (dev::BadHexCharacter) + { + return formatFatalError( + "JSONError", + "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied." + ); + } + } } m_compilerStack.setLibraries(libraries); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index c4caf20391fb..0bb941720b59 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -610,6 +610,29 @@ BOOST_AUTO_TEST_CASE(libraries_invalid_entry) BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); } +BOOST_AUTO_TEST_CASE(libraries_invalid_hex) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000xx1" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); +} + BOOST_AUTO_TEST_CASE(libraries_various_addresses) { char const* input = R"( From f41591b3ddfd3e5c065271058dadb3c6b7f31bbb Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 17 Jan 2018 21:02:23 +0100 Subject: [PATCH 071/242] [SMTChecker] A little refactoring on SSA vars --- libsolidity/formal/SMTChecker.cpp | 81 ++++++++++------------ libsolidity/formal/SMTChecker.h | 12 ++-- libsolidity/formal/SSAVariable.cpp | 66 ++++++++++++++++++ libsolidity/formal/SSAVariable.h | 79 +++++++++++++++++++++ libsolidity/formal/SymbolicIntVariable.cpp | 54 +++++++++++++++ libsolidity/formal/SymbolicIntVariable.h | 51 ++++++++++++++ libsolidity/formal/SymbolicVariable.cpp | 38 ++++++++++ libsolidity/formal/SymbolicVariable.h | 68 ++++++++++++++++++ 8 files changed, 395 insertions(+), 54 deletions(-) create mode 100644 libsolidity/formal/SSAVariable.cpp create mode 100644 libsolidity/formal/SSAVariable.h create mode 100644 libsolidity/formal/SymbolicIntVariable.cpp create mode 100644 libsolidity/formal/SymbolicIntVariable.h create mode 100644 libsolidity/formal/SymbolicVariable.cpp create mode 100644 libsolidity/formal/SymbolicVariable.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a64024b368f3..37dce96c9d09 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -23,6 +23,7 @@ #include #endif +#include #include #include @@ -69,8 +70,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) // We only handle local variables, so we clear at the beginning of the function. // If we add storage variables, those should be cleared differently. m_interface->reset(); - m_currentSequenceCounter.clear(); - m_nextFreeSequenceCounter.clear(); + m_variables.clear(); m_pathConditions.clear(); m_conditionalExecutionHappened = false; initializeLocalVariables(_function); @@ -91,14 +91,18 @@ bool SMTChecker::visit(IfStatement const& _node) checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); - auto countersEndFalse = m_currentSequenceCounter; auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); vector touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); + decltype(countersEndTrue) countersEndFalse; if (_node.falseStatement()) { countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement()); } + else + { + countersEndFalse = m_variables; + } mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse); @@ -152,7 +156,7 @@ bool SMTChecker::visit(ForStatement const& _node) checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE."); } - VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; + VariableSequenceCounters sequenceCountersStart = m_variables; m_interface->push(); if (_node.condition()) m_interface->addAssertion(expr(*_node.condition())); @@ -163,7 +167,7 @@ bool SMTChecker::visit(ForStatement const& _node) m_interface->pop(); m_conditionalExecutionHappened = true; - m_currentSequenceCounter = sequenceCountersStart; + std::swap(sequenceCountersStart, m_variables); resetVariables(touchedVariables); @@ -514,7 +518,7 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) { - VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; + VariableSequenceCounters beforeVars = m_variables; if (_condition) pushPathCondition(*_condition); @@ -523,8 +527,9 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s popPathCondition(); m_conditionalExecutionHappened = true; - std::swap(sequenceCountersStart, m_currentSequenceCounter); - return sequenceCountersStart; + std::swap(m_variables, beforeVars); + + return beforeVars; } void SMTChecker::checkCondition( @@ -709,8 +714,8 @@ void SMTChecker::mergeVariables(vector const& _variables, sm set uniqueVars(_variables.begin(), _variables.end()); for (auto const* decl: uniqueVars) { - int trueCounter = _countersEndTrue.at(decl); - int falseCounter = _countersEndFalse.at(decl); + int trueCounter = _countersEndTrue.at(decl).index(); + int falseCounter = _countersEndFalse.at(decl).index(); solAssert(trueCounter != falseCounter, ""); m_interface->addAssertion(newValue(*decl) == smt::Expression::ite( _condition, @@ -724,12 +729,8 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { if (dynamic_cast(_varDecl.type().get())) { - solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_variables.count(&_varDecl) == 0, ""); - m_currentSequenceCounter[&_varDecl] = 0; - m_nextFreeSequenceCounter[&_varDecl] = 1; - m_variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); return true; } else @@ -742,11 +743,6 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) } } -string SMTChecker::uniqueSymbol(Declaration const& _decl) -{ - return _decl.name() + "_" + to_string(_decl.id()); -} - string SMTChecker::uniqueSymbol(Expression const& _expr) { return "expr_" + to_string(_expr.id()); @@ -754,48 +750,38 @@ string SMTChecker::uniqueSymbol(Expression const& _expr) bool SMTChecker::knownVariable(Declaration const& _decl) { - return m_currentSequenceCounter.count(&_decl); + return m_variables.count(&_decl); } smt::Expression SMTChecker::currentValue(Declaration const& _decl) { - solAssert(m_currentSequenceCounter.count(&_decl), ""); - return valueAtSequence(_decl, m_currentSequenceCounter.at(&_decl)); + solAssert(knownVariable(_decl), ""); + return m_variables.at(&_decl)(); } -smt::Expression SMTChecker::valueAtSequence(const Declaration& _decl, int _sequence) +smt::Expression SMTChecker::valueAtSequence(Declaration const& _decl, int _sequence) { - return var(_decl)(_sequence); + solAssert(knownVariable(_decl), ""); + return m_variables.at(&_decl)(_sequence); } smt::Expression SMTChecker::newValue(Declaration const& _decl) { - solAssert(m_nextFreeSequenceCounter.count(&_decl), ""); - m_currentSequenceCounter[&_decl] = m_nextFreeSequenceCounter[&_decl]++; - return currentValue(_decl); + solAssert(knownVariable(_decl), ""); + ++m_variables.at(&_decl); + return m_variables.at(&_decl)(); } void SMTChecker::setZeroValue(Declaration const& _decl) { - solAssert(_decl.type()->category() == Type::Category::Integer, ""); - m_interface->addAssertion(currentValue(_decl) == 0); + solAssert(knownVariable(_decl), ""); + m_variables.at(&_decl).setZeroValue(); } void SMTChecker::setUnknownValue(Declaration const& _decl) { - auto const& intType = dynamic_cast(*_decl.type()); - m_interface->addAssertion(currentValue(_decl) >= minValue(intType)); - m_interface->addAssertion(currentValue(_decl) <= maxValue(intType)); -} - -smt::Expression SMTChecker::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SMTChecker::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); + solAssert(knownVariable(_decl), ""); + m_variables.at(&_decl).setUnknownValue(); } smt::Expression SMTChecker::expr(Expression const& _e) @@ -842,12 +828,15 @@ void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) m_interface->addAssertion(expr(_e) == _value); } -smt::Expression SMTChecker::var(Declaration const& _decl) +smt::Expression SMTChecker::minValue(IntegerType const& _t) { - solAssert(m_variables.count(&_decl), ""); - return m_variables.at(&_decl); + return smt::Expression(_t.minValue()); } +smt::Expression SMTChecker::maxValue(IntegerType const& _t) +{ + return smt::Expression(_t.maxValue()); +} void SMTChecker::popPathCondition() { solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index b57f0f96baec..7481e1c8f004 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -76,7 +78,7 @@ class SMTChecker: private ASTConstVisitor void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); /// Maps a variable to an SSA index. - using VariableSequenceCounters = std::map; + 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. @@ -118,7 +120,6 @@ class SMTChecker: private ASTConstVisitor /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); - static std::string uniqueSymbol(Declaration const& _decl); static std::string uniqueSymbol(Expression const& _expr); /// @returns true if _delc is a variable that is known at the current point, i.e. @@ -148,9 +149,6 @@ class SMTChecker: private ASTConstVisitor 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); /// Adds a new path condition void pushPathCondition(smt::Expression const& _e); @@ -166,10 +164,8 @@ class SMTChecker: private ASTConstVisitor std::shared_ptr m_interface; std::shared_ptr m_variableUsage; bool m_conditionalExecutionHappened = false; - std::map m_currentSequenceCounter; - std::map m_nextFreeSequenceCounter; std::map m_expressions; - std::map m_variables; + std::map m_variables; std::vector m_pathConditions; ErrorReporter& m_errorReporter; diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp new file mode 100644 index 000000000000..d6e97a8d3d96 --- /dev/null +++ b/libsolidity/formal/SSAVariable.cpp @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SSAVariable::SSAVariable(Declaration const* _decl, + smt::SolverInterface& _interface) +{ + resetIndex(); + + if (dynamic_cast(_decl->type().get())) + m_symbVar = make_shared(_decl, _interface); + else + { + //solAssert(false, ""); + } +} + +void SSAVariable::resetIndex() +{ + m_currentSequenceCounter = 0; + m_nextFreeSequenceCounter.reset (new int); + *m_nextFreeSequenceCounter = 1; +} + +int SSAVariable::index() const +{ + return m_currentSequenceCounter; +} + +int SSAVariable::next() const +{ + return *m_nextFreeSequenceCounter; +} + +void SSAVariable::setZeroValue() +{ + m_symbVar->setZeroValue(index()); +} + +void SSAVariable::setUnknownValue() +{ + m_symbVar->setUnknownValue(index()); +} diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h new file mode 100644 index 000000000000..3d7eb80c0b4b --- /dev/null +++ b/libsolidity/formal/SSAVariable.h @@ -0,0 +1,79 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class Declaration; + +/** + * This class represents the SSA representation of a program variable. + */ +class SSAVariable +{ +public: + explicit SSAVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SSAVariable(SSAVariable const&) = default; + SSAVariable(SSAVariable&&) = default; + SSAVariable& operator=(SSAVariable const&) = default; + SSAVariable& operator=(SSAVariable&&) = default; + + void resetIndex(); + + int index() const; + int next() const; + + int operator++() + { + return m_currentSequenceCounter = (*m_nextFreeSequenceCounter)++; + } + + smt::Expression operator()() const + { + return valueAtSequence(index()); + } + + smt::Expression operator()(int _seq) const + { + return valueAtSequence(_seq); + } + + void setZeroValue(); + void setUnknownValue(); + +private: + smt::Expression valueAtSequence(int _seq) const + { + return (*m_symbVar)(_seq); + } + + std::shared_ptr m_symbVar = nullptr; + int m_currentSequenceCounter; + std::shared_ptr m_nextFreeSequenceCounter; +}; + +} +} diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp new file mode 100644 index 000000000000..6ed7037f8bd4 --- /dev/null +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -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 . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicIntVariable::SymbolicIntVariable(Declaration const* _decl, + smt::SolverInterface&_interface) + : SymbolicVariable(_decl, _interface) +{ + solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); + m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); +} + +void SymbolicIntVariable::setZeroValue(int _seq) +{ + m_interface.addAssertion(valueAtSequence(_seq) == 0); +} + +void SymbolicIntVariable::setUnknownValue(int _seq) +{ + auto const& intType = dynamic_cast(*m_declaration->type()); + m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); + m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); +} + +smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) const +{ + return smt::Expression(_t.minValue()); +} + +smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) const +{ + return smt::Expression(_t.maxValue()); +} diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h new file mode 100644 index 000000000000..efe05af85d6b --- /dev/null +++ b/libsolidity/formal/SymbolicIntVariable.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 . +*/ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * Specialization of SymbolicVariable for Integers + */ +class SymbolicIntVariable : public SymbolicVariable +{ +public: + explicit SymbolicIntVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SymbolicIntVariable(SymbolicIntVariable const&) = default; + SymbolicIntVariable(SymbolicIntVariable&&) = default; + SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; + SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; + + void setZeroValue(int _seq); + void setUnknownValue(int _seq); + +private: + smt::Expression minValue(IntegerType const& _t) const; + smt::Expression maxValue(IntegerType const& _t) const; +}; + +} +} diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp new file mode 100644 index 000000000000..13f5d9b6eebb --- /dev/null +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -0,0 +1,38 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicVariable::SymbolicVariable(Declaration const* _decl, + smt::SolverInterface& _interface) + : m_declaration(_decl), + m_interface(_interface) +{ +} + +string SymbolicVariable::uniqueSymbol() const +{ + return m_declaration->name() + "_" + to_string(m_declaration->id()); +} + + diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h new file mode 100644 index 000000000000..4de595043245 --- /dev/null +++ b/libsolidity/formal/SymbolicVariable.h @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class Declaration; + +/** + * This class represents the symbolic version of a program variable. + */ +class SymbolicVariable +{ +public: + explicit SymbolicVariable(Declaration const* _decl, + smt::SolverInterface& _interface); + SymbolicVariable(SymbolicVariable const&) = default; + SymbolicVariable(SymbolicVariable&&) = default; + SymbolicVariable& operator=(SymbolicVariable const&) = default; + SymbolicVariable& operator=(SymbolicVariable&&) = default; + + smt::Expression operator()(int _seq) const + { + return valueAtSequence(_seq); + } + + std::string uniqueSymbol() const; + + virtual void setZeroValue(int _seq) = 0; + virtual void setUnknownValue(int _seq) = 0; + +protected: + smt::Expression valueAtSequence(int _seq) const + { + return (*m_expression)(_seq); + } + + Declaration const* m_declaration; + std::shared_ptr m_expression = nullptr; + smt::SolverInterface& m_interface; +}; + +} +} From 3b2851ee4163bcfbca9e4e23650dfeee1a06653a Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sat, 17 Feb 2018 09:34:38 +0100 Subject: [PATCH 072/242] Integer min and max values placed under SymbolicIntVar instead of SMTChecker --- libsolidity/formal/SMTChecker.cpp | 14 +++----------- libsolidity/formal/SMTChecker.h | 3 --- libsolidity/formal/SymbolicIntVariable.cpp | 4 ++-- libsolidity/formal/SymbolicIntVariable.h | 7 ++++--- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 37dce96c9d09..ad42c10518e1 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -244,14 +245,14 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) { checkCondition( - _value < minValue(_type), + _value < SymbolicIntVariable::minValue(_type), _location, "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", "value", &_value ); checkCondition( - _value > maxValue(_type), + _value > SymbolicIntVariable::maxValue(_type), _location, "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", "value", @@ -828,15 +829,6 @@ void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) m_interface->addAssertion(expr(_e) == _value); } -smt::Expression SMTChecker::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SMTChecker::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); -} void SMTChecker::popPathCondition() { solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 7481e1c8f004..7e7996cfedf2 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -140,9 +140,6 @@ class SMTChecker: private ASTConstVisitor /// Resets the variable to an unknown value (in its range). void setUnknownValue(Declaration const& decl); - static smt::Expression minValue(IntegerType const& _t); - static smt::Expression maxValue(IntegerType const& _t); - /// 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/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index 6ed7037f8bd4..c206f1cd6ff2 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -43,12 +43,12 @@ void SymbolicIntVariable::setUnknownValue(int _seq) m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); } -smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) const +smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) { return smt::Expression(_t.minValue()); } -smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) const +smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) { return smt::Expression(_t.maxValue()); } diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index efe05af85d6b..0066bb7513c0 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -39,12 +39,13 @@ class SymbolicIntVariable : public SymbolicVariable SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; + /// Sets the var to 0. void setZeroValue(int _seq); + /// Sets the valid interval for the var. void setUnknownValue(int _seq); -private: - smt::Expression minValue(IntegerType const& _t) const; - smt::Expression maxValue(IntegerType const& _t) const; + static smt::Expression minValue(IntegerType const& _t); + static smt::Expression maxValue(IntegerType const& _t); }; } From 21c6b80fc98f6d584f240a47d4a01827768f18f3 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sat, 17 Feb 2018 09:35:37 +0100 Subject: [PATCH 073/242] Supported types listed in SSAVariable --- libsolidity/formal/SMTChecker.cpp | 4 ++-- libsolidity/formal/SSAVariable.cpp | 7 ++++++- libsolidity/formal/SSAVariable.h | 9 +++++++++ libsolidity/formal/SymbolicVariable.h | 3 +++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index ad42c10518e1..1da5b291ea0a 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -370,7 +370,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())) + else if (SSAVariable::supportedType(_identifier.annotation().type.get())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -728,7 +728,7 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (dynamic_cast(_varDecl.type().get())) + if (SSAVariable::supportedType(_varDecl.type().get())) { solAssert(m_variables.count(&_varDecl) == 0, ""); m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index d6e97a8d3d96..7f214687b624 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -34,10 +34,15 @@ SSAVariable::SSAVariable(Declaration const* _decl, m_symbVar = make_shared(_decl, _interface); else { - //solAssert(false, ""); + solAssert(false, ""); } } +bool SSAVariable::supportedType(Type const* _decl) +{ + return dynamic_cast(_decl); +} + void SSAVariable::resetIndex() { m_currentSequenceCounter = 0; diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 3d7eb80c0b4b..b87693c2601b 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -34,6 +34,8 @@ class Declaration; class SSAVariable { public: + /// @param _decl Used to determine the type and forwarded to the symbolic var. + /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. explicit SSAVariable(Declaration const* _decl, smt::SolverInterface& _interface); SSAVariable(SSAVariable const&) = default; @@ -61,9 +63,14 @@ class SSAVariable return valueAtSequence(_seq); } + /// These two functions forward the call to the symbolic var + /// which generates the constraints according to the type. void setZeroValue(); void setUnknownValue(); + /// So far Int is supported. + static bool supportedType(Type const* _decl); + private: smt::Expression valueAtSequence(int _seq) const { @@ -72,6 +79,8 @@ class SSAVariable std::shared_ptr m_symbVar = nullptr; int m_currentSequenceCounter; + /// The next free sequence counter is a shared pointer because we want + /// the copy and the copied to share it. std::shared_ptr m_nextFreeSequenceCounter; }; diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 4de595043245..66633b7353bf 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -50,7 +50,10 @@ class SymbolicVariable std::string uniqueSymbol() const; + /// Sets the var to the default value of its type. virtual void setZeroValue(int _seq) = 0; + /// The unknown value depends on the type. For example, an interval is set for Integers. + /// This is decided by the subclasses. virtual void setUnknownValue(int _seq) = 0; protected: From cff0836c032ecee2710f1c17c49eec0a3b4aa9fc Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 28 Feb 2018 18:00:13 +0100 Subject: [PATCH 074/242] Fix PR comments --- libsolidity/formal/SSAVariable.cpp | 12 +++++++----- libsolidity/formal/SSAVariable.h | 12 ++++++++---- libsolidity/formal/SymbolicIntVariable.cpp | 8 +++++--- libsolidity/formal/SymbolicIntVariable.h | 10 ++++++---- libsolidity/formal/SymbolicVariable.cpp | 8 +++++--- libsolidity/formal/SymbolicVariable.h | 10 ++++++---- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 7f214687b624..4e6bcbcb5cbd 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -25,13 +25,15 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SSAVariable::SSAVariable(Declaration const* _decl, - smt::SolverInterface& _interface) +SSAVariable::SSAVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +) { resetIndex(); if (dynamic_cast(_decl->type().get())) - m_symbVar = make_shared(_decl, _interface); + m_symbolicVar = make_shared(_decl, _interface); else { solAssert(false, ""); @@ -62,10 +64,10 @@ int SSAVariable::next() const void SSAVariable::setZeroValue() { - m_symbVar->setZeroValue(index()); + m_symbolicVar->setZeroValue(index()); } void SSAVariable::setUnknownValue() { - m_symbVar->setUnknownValue(index()); + m_symbolicVar->setUnknownValue(index()); } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index b87693c2601b..4ec92aa1966d 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -36,8 +36,10 @@ class SSAVariable public: /// @param _decl Used to determine the type and forwarded to the symbolic var. /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. - explicit SSAVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SSAVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SSAVariable(SSAVariable const&) = default; SSAVariable(SSAVariable&&) = default; SSAVariable& operator=(SSAVariable const&) = default; @@ -45,7 +47,9 @@ class SSAVariable void resetIndex(); + /// This function returns the current index of this SSA variable. int index() const; + /// This function returns the next free index of this SSA variable. int next() const; int operator++() @@ -74,10 +78,10 @@ class SSAVariable private: smt::Expression valueAtSequence(int _seq) const { - return (*m_symbVar)(_seq); + return (*m_symbolicVar)(_seq); } - std::shared_ptr m_symbVar = nullptr; + std::shared_ptr m_symbolicVar = nullptr; int m_currentSequenceCounter; /// The next free sequence counter is a shared pointer because we want /// the copy and the copied to share it. diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index c206f1cd6ff2..d08dc15537e3 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -23,9 +23,11 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SymbolicIntVariable::SymbolicIntVariable(Declaration const* _decl, - smt::SolverInterface&_interface) - : SymbolicVariable(_decl, _interface) +SymbolicIntVariable::SymbolicIntVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +): + SymbolicVariable(_decl, _interface) { solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index 0066bb7513c0..8a9b5d5ded85 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -29,11 +29,13 @@ namespace solidity /** * Specialization of SymbolicVariable for Integers */ -class SymbolicIntVariable : public SymbolicVariable +class SymbolicIntVariable: public SymbolicVariable { public: - explicit SymbolicIntVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SymbolicIntVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SymbolicIntVariable(SymbolicIntVariable const&) = default; SymbolicIntVariable(SymbolicIntVariable&&) = default; SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; @@ -41,7 +43,7 @@ class SymbolicIntVariable : public SymbolicVariable /// Sets the var to 0. void setZeroValue(int _seq); - /// Sets the valid interval for the var. + /// Sets the variable to the full valid value range. void setUnknownValue(int _seq); static smt::Expression minValue(IntegerType const& _t); diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp index 13f5d9b6eebb..629049ea51e2 100644 --- a/libsolidity/formal/SymbolicVariable.cpp +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -23,9 +23,11 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SymbolicVariable::SymbolicVariable(Declaration const* _decl, - smt::SolverInterface& _interface) - : m_declaration(_decl), +SymbolicVariable::SymbolicVariable( + Declaration const* _decl, + smt::SolverInterface& _interface +): + m_declaration(_decl), m_interface(_interface) { } diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 66633b7353bf..2b59e57aa45d 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -36,8 +36,10 @@ class Declaration; class SymbolicVariable { public: - explicit SymbolicVariable(Declaration const* _decl, - smt::SolverInterface& _interface); + SymbolicVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); SymbolicVariable(SymbolicVariable const&) = default; SymbolicVariable(SymbolicVariable&&) = default; SymbolicVariable& operator=(SymbolicVariable const&) = default; @@ -52,8 +54,8 @@ class SymbolicVariable /// Sets the var to the default value of its type. virtual void setZeroValue(int _seq) = 0; - /// The unknown value depends on the type. For example, an interval is set for Integers. - /// This is decided by the subclasses. + /// The unknown value is the full range of valid values, + /// and that's sub-type dependent. virtual void setUnknownValue(int _seq) = 0; protected: From 8b1b4b78c043d54f493dc9e2330bf9df5cf93755 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 28 Feb 2018 18:31:11 +0100 Subject: [PATCH 075/242] Fix PR comments --- libsolidity/formal/SSAVariable.h | 4 ---- libsolidity/formal/SymbolicIntVariable.h | 4 ---- libsolidity/formal/SymbolicVariable.h | 4 ---- 3 files changed, 12 deletions(-) diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 4ec92aa1966d..275e85900a1f 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -40,10 +40,6 @@ class SSAVariable Declaration const* _decl, smt::SolverInterface& _interface ); - SSAVariable(SSAVariable const&) = default; - SSAVariable(SSAVariable&&) = default; - SSAVariable& operator=(SSAVariable const&) = default; - SSAVariable& operator=(SSAVariable&&) = default; void resetIndex(); diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index 8a9b5d5ded85..afa25f1b92d3 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -36,10 +36,6 @@ class SymbolicIntVariable: public SymbolicVariable Declaration const* _decl, smt::SolverInterface& _interface ); - SymbolicIntVariable(SymbolicIntVariable const&) = default; - SymbolicIntVariable(SymbolicIntVariable&&) = default; - SymbolicIntVariable& operator=(SymbolicIntVariable const&) = default; - SymbolicIntVariable& operator=(SymbolicIntVariable&&) = default; /// Sets the var to 0. void setZeroValue(int _seq); diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 2b59e57aa45d..9325825036f6 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -40,10 +40,6 @@ class SymbolicVariable Declaration const* _decl, smt::SolverInterface& _interface ); - SymbolicVariable(SymbolicVariable const&) = default; - SymbolicVariable(SymbolicVariable&&) = default; - SymbolicVariable& operator=(SymbolicVariable const&) = default; - SymbolicVariable& operator=(SymbolicVariable&&) = default; smt::Expression operator()(int _seq) const { From b6dfd9ef5422d80177b622fc2c486de00fcc0f73 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 16:57:35 +0100 Subject: [PATCH 076/242] Ensure that library addresses supplied are of correct length and hex prefixed in JSONIO --- Changelog.md | 3 +- libsolidity/interface/StandardCompiler.cpp | 20 +++++++++++-- test/libsolidity/StandardCompiler.cpp | 34 +++++++++++++++++----- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index e027e8ad4f38..4262f8397847 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,7 +15,8 @@ Bugfixes: * JSON-AST: Add "documentation" property to function, event and modifier definition. * Resolver: Properly determine shadowing for imports with aliases. * Standalone Assembly: Do not ignore input after closing brace of top level block. - * Standard JSON: catch errors properly when invalid "sources" are passed + * Standard JSON: Catch errors properly when invalid "sources" are passed. + * Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed. * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. * Commandline interface: throw error if option is unknown diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 8c64c16462de..91fe72ae2d77 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; using namespace dev; using namespace dev::solidity; @@ -337,16 +339,30 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) { + string address = jsonSourceName[library].asString(); + + if (!boost::starts_with(address, "0x")) + return formatFatalError( + "JSONError", + "Library address is not prefixed with \"0x\"." + ); + + if (address.length() != 42) + return formatFatalError( + "JSONError", + "Library address is of invalid length." + ); + try { // @TODO use libraries only for the given source - libraries[library] = h160(jsonSourceName[library].asString()); + libraries[library] = h160(address); } catch (dev::BadHexCharacter) { return formatFatalError( "JSONError", - "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied." + "Invalid library address (\"" + address + "\") supplied." ); } } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 0bb941720b59..eb2773ba1fb3 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -633,7 +633,7 @@ BOOST_AUTO_TEST_CASE(libraries_invalid_hex) BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); } -BOOST_AUTO_TEST_CASE(libraries_various_addresses) +BOOST_AUTO_TEST_CASE(libraries_invalid_length) { char const* input = R"( { @@ -641,11 +641,8 @@ BOOST_AUTO_TEST_CASE(libraries_various_addresses) "settings": { "libraries": { "library.sol": { - "L": 42, - "L3": "42", - "L4": "0x42", - "L5": "0x4200000000000000000000000000000000000001", - "L6": "4200000000000000000000000000000000000001" + "L1": "0x42", + "L2": "0x4200000000000000000000000000000000000001ff" } } }, @@ -657,7 +654,30 @@ BOOST_AUTO_TEST_CASE(libraries_various_addresses) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsAtMostWarnings(result)); + BOOST_CHECK(containsError(result, "JSONError", "Library address is of invalid length.")); +} + +BOOST_AUTO_TEST_CASE(libraries_missing_hex_prefix) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Library address is not prefixed with \"0x\".")); } BOOST_AUTO_TEST_CASE(library_linking) From 754076319659a387a2c447fe69285aad07e0abb6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:20:49 +0100 Subject: [PATCH 077/242] Tests for multi-dimensional arrays. --- .../SolidityNameAndTypeResolution.cpp | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853ceaba..1fadcbde6cf4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -979,6 +979,62 @@ BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_ne CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); } +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (string[][]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_multi_dimensional_static_arrays) +{ + char const* text = R"( + contract C { + function f() public pure returns (uint[][2]) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_new_abi) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + + contract C { + struct S { string[] s; } + function f() public pure returns (S) {} + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_arrays) +{ + char const* text = R"( + contract C { + struct S { string[] s; } + function f() public pure returns (S x) {} + } + )"; + CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); +} + BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { char const* text = R"( From 08b6a72d37f4e4149575bfc695d76c61b666a8a9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:21:02 +0100 Subject: [PATCH 078/242] Fix multi-dimensional arrays in the ABI. --- libsolidity/analysis/TypeChecker.cpp | 30 ++++++++++++++++++++++++---- libsolidity/ast/Types.cpp | 2 -- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472afe5f..a2d94be48aba 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -34,6 +34,29 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + +bool typeSupportedByOldABIEncoder(Type const& _type) +{ + if (_type.dataStoredIn(DataLocation::Storage)) + return true; + else if (_type.category() == Type::Category::Struct) + return false; + else if (_type.category() == Type::Category::Array) + { + auto const& arrayType = dynamic_cast(_type); + auto base = arrayType.baseType(); + if (!typeSupportedByOldABIEncoder(*base)) + return false; + else if (base->category() == Type::Category::Array && base->isDynamicallySized()) + return false; + } + return true; +} + +} + bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) { @@ -561,13 +584,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) 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 && - !type(*var)->dataStoredIn(DataLocation::Storage) && - !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) + !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + !typeSupportedByOldABIEncoder(*type(*var)) ) m_errorReporter.typeError( var->location(), - "Structs are only supported in the new experimental ABI encoder. " + "This type is only supported in the new experimental ABI encoder. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 771ae64393e0..6a9707f3e824 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1589,8 +1589,6 @@ bool ArrayType::canBeUsedExternally(bool _inLibrary) const return true; else if (!m_baseType->canBeUsedExternally(_inLibrary)) return false; - else if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized()) - return false; else return true; } From 98e8a9385456b5d3fe3ca7ef2e0c923b0aab2ea3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Feb 2018 18:45:42 +0100 Subject: [PATCH 079/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 4262f8397847..ebdb2e4ed223 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,7 @@ Bugfixes: * Standalone Assembly: Do not ignore input after closing brace of top level block. * Standard JSON: Catch errors properly when invalid "sources" are passed. * Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed. + * Type Checker: Properly detect which array and struct types are unsupported by the old ABI encoder. * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. * Commandline interface: throw error if option is unknown From a0d006015e253b88626d2489e8cd8b5b537f5079 Mon Sep 17 00:00:00 2001 From: Oleksii Matiiasevych Date: Thu, 1 Mar 2018 22:54:04 +0700 Subject: [PATCH 080/242] Update Tips and Tricks on structs initialization. (#3626) * Update Tips and Tricks on structs initialization. --- docs/miscellaneous.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index b5d605ac33ce..70ed6201da26 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -230,7 +230,10 @@ Tips and Tricks * Make your state variables public - the compiler will create :ref:`getters ` for you automatically. * If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`. * If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``. -* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` +* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` + +.. note:: + If the storage struct has tightly packed properties, initialize it with separate assignments: ``x.a = 1; x.b = 2;``. In this way it will be easier for the optimizer to update storage in one go, thus making assignment cheaper. ********** Cheatsheet From bd4c2b9bde02b94d367ad9af6e44b9434bb1bb93 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 1 Mar 2018 16:58:20 +0100 Subject: [PATCH 081/242] Deprecate using unit denominations in combination with hex numbers. Closes #3574. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 15 +++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/Changelog.md b/Changelog.md index e027e8ad4f38..47e6c43796a4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. + * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472afe5f..4ff0fb8f531b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2021,6 +2021,8 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(Literal const& _literal) { + bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_literal.looksLikeAddress()) { if (_literal.passesAddressChecksum()) @@ -2034,6 +2036,19 @@ void TypeChecker::endVisit(Literal const& _literal) "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals" ); } + if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) + { + if (v050) + m_errorReporter.fatalTypeError( + _literal.location(), + "Hexadecimal numbers cannot be used with unit denominations." + ); + else + m_errorReporter.warning( + _literal.location(), + "Hexadecimal numbers with unit denominations are deprecated." + ); + } if (!_literal.annotation().type) _literal.annotation().type = Type::forLiteral(_literal); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853ceaba..eeefe818e48d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2717,6 +2717,25 @@ BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(combining_hex_and_denomination) +{ + char const* text = R"( + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_WARNING(text, "Hexadecimal numbers with unit denominations are deprecated."); + + char const* textV050 = R"( + pragma experimental "v0.5.0"; + + contract Foo { + uint constant x = 0x01 wei; + } + )"; + CHECK_ERROR(textV050, TypeError, "Hexadecimal numbers cannot be used with unit denominations."); +} + BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) { char const* text = R"( From c9840c98f45e6fa9258ec4624219622f5f71c75c Mon Sep 17 00:00:00 2001 From: Elena Dimitrova Date: Thu, 1 Mar 2018 17:59:47 +0200 Subject: [PATCH 082/242] Documentation updates for internal constructors and function signature (#3365) * Add a note explaining return values not included in function signature * Add section on Constructors in documentation * Improve documented definition for abstract contract * Add benefits of abstraction to documentation --- docs/abi-spec.rst | 6 ++++++ docs/contracts.rst | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 07c8e0ce7a63..4a61d91f0297 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -26,6 +26,10 @@ The first four bytes of the call data for a function call specifies the function first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used. +.. note:: + The return type of a function is not part of this signature. In :ref:`Solidity's function overloading ` return types are not considered. The reason is to keep function call resolution context-independent. + The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI `) + Argument Encoding ================= @@ -290,6 +294,8 @@ In effect, a log entry using this ABI is described as: 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. +.. _abi_json: + JSON ==== diff --git a/docs/contracts.rst b/docs/contracts.rst index 416dc6499bce..967eb2c84711 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -955,6 +955,31 @@ not known in the context of the class where it is used, although its type is known. This is similar for ordinary virtual method lookup. +.. index:: ! constructor + +Constructors +============ +A constructor is an optional function with the same name as the contract which is executed upon contract creation. +Constructor functions can be either ``public`` or ``internal``. + +:: + + pragma solidity ^0.4.11; + + contract A { + uint public a; + + function A(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + function B() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. + .. index:: ! base;constructor Arguments for Base Constructors @@ -1027,11 +1052,13 @@ As an exception, a state variable getter can override a public function. .. index:: ! contract;abstract, ! abstract contract +.. _abstract-contract: + ****************** Abstract Contracts ****************** -Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by ``;``):: +Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: pragma solidity ^0.4.0; @@ -1039,9 +1066,7 @@ Contract functions can lack an implementation as in the following example (note function utterance() public returns (bytes32); } -Such contracts cannot be compiled (even if they contain -implemented functions alongside non-implemented functions), -but they can be used as base contracts:: +Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: pragma solidity ^0.4.0; @@ -1065,6 +1090,8 @@ Example of a Function Type (a variable declaration, where the variable is of typ function(address) external returns (address) foo; +Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and +facilitating patterns like the `Template method `_ and removing code duplication. .. index:: ! contract;interface, ! interface contract From 5ab4a1ae7819004415293bf72a86824beb43cd51 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:43:40 +0100 Subject: [PATCH 083/242] Add ability to set the target EVM version. --- libsolidity/interface/CompilerStack.cpp | 7 ++ libsolidity/interface/CompilerStack.h | 27 +++++--- libsolidity/interface/EVMVersion.h | 81 ++++++++++++++++++++++ libsolidity/interface/StandardCompiler.cpp | 8 +++ solc/CommandLineInterface.cpp | 24 ++++++- 5 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 libsolidity/interface/EVMVersion.h diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3b5e65e8c131..7bc31c17bdac 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector const& _remappings) swap(m_remappings, remappings); } +void CompilerStack::setEVMVersion(EVMVersion _version) +{ + solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing."); + m_evmVersion = _version; +} + void CompilerStack::reset(bool _keepSources) { if (_keepSources) @@ -88,6 +94,7 @@ void CompilerStack::reset(bool _keepSources) m_sources.clear(); } m_libraries.clear(); + m_evmVersion = EVMVersion(); m_optimize = false; m_optimizeRuns = 200; m_globalContext.reset(); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index b377b3aa86d9..13c9cc7a1a49 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -23,20 +23,26 @@ #pragma once +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace dev { @@ -116,6 +122,8 @@ class CompilerStack: boost::noncopyable m_optimizeRuns = _runs; } + void setEVMVersion(EVMVersion _version = EVMVersion{}); + /// Sets the list of requested contract names. If empty, no filtering is performed and every contract /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. void setRequestedContractNames(std::set const& _contractNames = std::set{}) @@ -310,6 +318,7 @@ class CompilerStack: boost::noncopyable ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; + EVMVersion m_evmVersion; std::set m_requestedContractNames; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h new file mode 100644 index 000000000000..1ddcd218bc0d --- /dev/null +++ b/libsolidity/interface/EVMVersion.h @@ -0,0 +1,81 @@ +/* + 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 . +*/ +/** + * EVM versioning. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * A version specifier of the EVM we want to compile to. + * Defaults to the latest version. + */ +class EVMVersion +{ +public: + EVMVersion() {} + + static EVMVersion homestead() { return {Version::Homestead}; } + static EVMVersion byzantium() { return {Version::Byzantium}; } + + static boost::optional fromString(std::string const& _version) + { + if (_version == "homestead") + return homestead(); + else if (_version == "byzantium") + return byzantium(); + else + return {}; + } + + bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } + bool operator!=(EVMVersion const& _other) const { return !this->operator==(_other); } + + std::string name() const { return m_version == Version::Byzantium ? "byzantium" : "homestead"; } + + /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. + bool supportsReturndata() const { return *this >= byzantium(); } + bool hasStaticCall() const { return *this >= byzantium(); } + + /// Whether we have to retain the costs for the call opcode itself (false), + /// or whether we can just forward easily all remaining gas (true). + bool canOverchargeGasForCall() const + { + // @TODO when exactly was this introduced? Was together with the call stack fix. + return m_version == Version::Byzantium; + } + +private: + enum class Version { Homestead, Byzantium }; + + EVMVersion(Version _version): m_version(_version) {} + + Version m_version = Version::Byzantium; +}; + + +} +} diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 91fe72ae2d77..ee9b14406e77 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -318,6 +318,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value const& settings = _input.get("settings", Json::Value()); + if (settings.isMember("evmVersion")) + { + boost::optional version = EVMVersion::fromString(settings.get("evmVersion", {}).asString()); + if (!version) + return formatFatalError("JSONError", "Invalid EVM version requested."); + m_compilerStack.setEVMVersion(*version); + } + vector remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) remappings.push_back(remapping.asString()); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 62b24975561a..04d6d1a8d905 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -71,7 +71,6 @@ namespace solidity static string const g_stdinFileNameStr = ""; static string const g_strAbi = "abi"; -static string const g_strAddStandard = "add-std"; static string const g_strAllowPaths = "allow-paths"; static string const g_strAsm = "asm"; static string const g_strAsmJson = "asm-json"; @@ -87,6 +86,7 @@ static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; +static string const g_strEVMVersion = "evm-version"; static string const g_streWasm = "ewasm"; static string const g_strFormal = "formal"; static string const g_strGas = "gas"; @@ -118,7 +118,6 @@ static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; static string const g_argAbi = g_strAbi; -static string const g_argAddStandard = g_strAddStandard; static string const g_argPrettyJson = g_strPrettyJson; static string const g_argAllowPaths = g_strAllowPaths; static string const g_argAsm = g_strAsm; @@ -537,13 +536,17 @@ Allowed options)", (g_argHelp.c_str(), "Show help message and exit.") (g_argVersion.c_str(), "Show version and exit.") (g_strLicense.c_str(), "Show licensing information and exit.") + ( + g_strEVMVersion.c_str(), + po::value()->value_name("version"), + "Select desired EVM version. Either homestead or byzantium (default)." + ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( g_argOptimizeRuns.c_str(), po::value()->value_name("n")->default_value(200), "Estimated number of contract runs for optimizer tuning." ) - (g_argAddStandard.c_str(), "Add standard contracts.") (g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.") ( g_argLibraries.c_str(), @@ -779,6 +782,19 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); + EVMVersion evmVersion; + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + boost::optional versionOption = EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return false; + } + evmVersion = *versionOption; + } + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; SourceReferenceFormatter formatter(cerr, scannerFromSourceName); @@ -792,6 +808,8 @@ bool CommandLineInterface::processInput() m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); + if (m_args.count(g_strEVMVersion)) + m_compiler->setEVMVersion(evmVersion); // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count(g_argOptimize) > 0; unsigned runs = m_args[g_argOptimizeRuns].as(); From 85785710e6182a5abb68e5339f052b4a451d086e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:45:08 +0100 Subject: [PATCH 084/242] Store EVM version in settings. --- libsolidity/interface/CompilerStack.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7bc31c17bdac..51dcf6f42a5e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -845,6 +845,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const } meta["settings"]["optimizer"]["enabled"] = m_optimize; meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; + meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = _contract.contract->annotation().canonicalName; From f75a41132553d073202d8bc2ea65cc287c7085b7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:56:42 +0100 Subject: [PATCH 085/242] Provide target EVM version in CompilerContext. --- libsolidity/codegen/Compiler.h | 13 ++++++++----- libsolidity/codegen/CompilerContext.h | 9 ++++++++- libsolidity/codegen/ContractCompiler.h | 2 +- libsolidity/interface/CompilerStack.cpp | 4 ++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 06654486a64d..f6865d757c1f 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -22,22 +22,25 @@ #pragma once -#include -#include #include +#include + #include +#include +#include + namespace dev { namespace solidity { class Compiler { public: - explicit Compiler(bool _optimize = false, unsigned _runs = 200): + explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200): m_optimize(_optimize), m_optimizeRuns(_runs), - m_runtimeContext(), - m_context(&m_runtimeContext) + m_runtimeContext(_evmVersion), + m_context(_evmVersion, &m_runtimeContext) { } /// Compiles a contract. diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index a155a3a5194c..e6b2484a358d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -24,6 +24,8 @@ #include +#include + #include #include #include @@ -50,14 +52,17 @@ namespace solidity { class CompilerContext { public: - explicit CompilerContext(CompilerContext* _runtimeContext = nullptr): + explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr): m_asm(std::make_shared()), + m_evmVersion(_evmVersion), m_runtimeContext(_runtimeContext) { if (m_runtimeContext) m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } + EVMVersion const& evmVersion() const { return m_evmVersion; } + /// Update currently enabled set of experimental features. void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } /// @returns true if the given feature is enabled. @@ -287,6 +292,8 @@ class CompilerContext } m_functionCompilationQueue; eth::AssemblyPointer m_asm; + /// Version of the EVM to compile against. + EVMVersion m_evmVersion; /// Activated experimental features. std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index d698dc71a0ad..18f319670650 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -43,7 +43,7 @@ class ContractCompiler: private ASTConstVisitor m_runtimeCompiler(_runtimeCompiler), m_context(_context) { - m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr); + m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr); } void compileContract( diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 51dcf6f42a5e..2f7338253ba5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -684,7 +684,7 @@ void CompilerStack::compileContract( for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); - shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); + shared_ptr compiler = make_shared(m_evmVersion, m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); string metadata = createMetadata(compiledContract); bytes cborEncodedHash = @@ -743,7 +743,7 @@ void CompilerStack::compileContract( { if (!_contract.isLibrary()) { - Compiler cloneCompiler(m_optimize, m_optimizeRuns); + Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns); cloneCompiler.compileClone(_contract, _compiledContracts); compiledContract.cloneObject = cloneCompiler.assembledObject(); } From 60f7be5d4cee94935ad35e3ebacd419e5b52ecce Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 14:47:36 +0100 Subject: [PATCH 086/242] Some tests. --- test/Metadata.cpp | 3 ++- test/libsolidity/StandardCompiler.cpp | 33 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 1ebfd46829cb..c130d3462a69 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -60,7 +60,8 @@ bool isValidMetadata(string const& _metadata) !metadata.isMember("compiler") || !metadata.isMember("settings") || !metadata.isMember("sources") || - !metadata.isMember("output") + !metadata.isMember("output") || + !metadata["settings"].isMember("evmVersion") ) return false; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index eb2773ba1fb3..df3ece7c0414 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -724,6 +724,39 @@ BOOST_AUTO_TEST_CASE(library_linking) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); } +BOOST_AUTO_TEST_CASE(evm_version) +{ + auto inputForVersion = [](string const& _version) + { + return R"( + { + "language": "Solidity", + "sources": { "fileA": { "content": "contract A { }" } }, + "settings": { + )" + _version + R"( + "outputSelection": { + "fileA": { + "A": [ "metadata" ] + } + } + } + } + )"; + }; + Json::Value result; + result = compile(inputForVersion("\"evmVersion\": \"homestead\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test default + result = compile(inputForVersion("")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + // test invalid + result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); + BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); +} + + BOOST_AUTO_TEST_SUITE_END() } From 83515eadcf21decc9355c69c621c4d530a62b04e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 14:52:16 +0100 Subject: [PATCH 087/242] Changelog entry --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 4262f8397847..9e3005dd5caf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. From f2f61f1c2f3b4d553c07fcf1746f49e799cb7aa4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 17:21:26 +0100 Subject: [PATCH 088/242] Test both EVM versions. --- circle.yml | 8 ----- scripts/tests.sh | 35 ++++++++++++++----- test/ExecutionFramework.cpp | 15 ++++++-- test/ExecutionFramework.h | 3 ++ test/TestHelper.cpp | 11 +++++- test/TestHelper.h | 6 +++- test/libsolidity/SolidityExecutionFramework.h | 1 + 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/circle.yml b/circle.yml index add8a81547e0..1ed09ada0281 100644 --- a/circle.yml +++ b/circle.yml @@ -5,10 +5,6 @@ jobs: - image: trzeci/emscripten:sdk-tag-1.37.21-64bit steps: - checkout - - run: - name: Init submodules - command: | - git submodule update --init - restore_cache: name: Restore Boost build key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} @@ -94,10 +90,6 @@ jobs: command: | apt-get -qq update apt-get -qy install ccache cmake libboost-all-dev libz3-dev - - run: - name: Init submodules - command: | - git submodule update --init - run: name: Store commit hash and prerelease command: | diff --git a/scripts/tests.sh b/scripts/tests.sh index 3c80adc5d507..69b2b338e33e 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -37,11 +37,9 @@ then echo "Usage: $0 [--junit_report ]" exit 1 fi - testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml" - testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml" + log_directory="$2" else - testargs_no_opt='' - testargs_opt='' + log_directory="" fi echo "Running commandline tests..." @@ -98,10 +96,31 @@ then progress="" fi -echo "--> Running tests without optimizer..." -"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc -echo "--> Running tests WITH optimizer..." -"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc +ERROR_CODE=0 +# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer +# and homestead / byzantium VM, # pointing to that IPC endpoint. +for optimize in "" "--optimize" +do + for vm in homestead byzantium + do + echo "--> Running tests using "$optimize" --evm-version "$vm"..." + log="" + if [ -n "$log_directory" ] + then + if [ -n "$optimize" ] + then + log=--logger=JUNIT,test_suite,$log_directory/opt_$vm.xml $testargs + else + log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt + fi + fi + set +e + "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc + THIS_ERR=$? + set -e + if [ $? -ne 0 ]; then ERROR_CODE=$?; fi + done +done wait $CMDLINE_PID diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 85b5bd3bb8f7..adf514e218dd 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -20,13 +20,15 @@ * Framework for executing contracts and testing them using RPC. */ -#include -#include -#include #include +#include + +#include #include +#include + using namespace std; using namespace dev; using namespace dev::test; @@ -53,6 +55,13 @@ ExecutionFramework::ExecutionFramework() : m_showMessages(dev::test::Options::get().showMessages), m_sender(m_rpc.account(0)) { + if (!dev::test::Options::get().evmVersion.empty()) + { + auto version = solidity::EVMVersion::fromString(dev::test::Options::get().evmVersion); + BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + dev::test::Options::get().evmVersion); + m_evmVersion = *version; + } + m_rpc.test_rewindToBlock(0); } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 8aa994738bea..a7971b8108ee 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -227,6 +229,7 @@ class ExecutionFramework bytes data; }; + solidity::EVMVersion m_evmVersion; unsigned m_optimizeRuns = 200; bool m_optimize = false; bool m_showMessages = false; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index c8747a06a42c..fbf2dc90168c 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -19,8 +19,12 @@ * @date 2014 */ +#include + +#include + #include -#include "TestHelper.h" + using namespace std; using namespace dev::test; @@ -41,6 +45,11 @@ Options::Options() } else if (string(suite.argv[i]) == "--optimize") optimize = true; + else if (string(suite.argv[i]) == "--evm-version") + { + evmVersion = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; + ++i; + } else if (string(suite.argv[i]) == "--show-messages") showMessages = true; else if (string(suite.argv[i]) == "--no-ipc") diff --git a/test/TestHelper.h b/test/TestHelper.h index d25c5cd819eb..69ac458a8fdc 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -19,11 +19,14 @@ #pragma once -#include +#include + #include #include #include +#include + namespace dev { namespace test @@ -33,6 +36,7 @@ struct Options: boost::noncopyable { std::string ipcPath; bool showMessages = false; + std::string evmVersion; bool optimize = false; bool disableIPC = false; bool disableSMT = false; diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index f562721d3dea..12687dd1d1c5 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -68,6 +68,7 @@ class SolidityExecutionFramework: public dev::test::ExecutionFramework m_compiler.reset(false); m_compiler.addSource("", sourceCode); m_compiler.setLibraries(_libraryAddresses); + m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { From a53d6b499d5cc5c45fc096cea6393dc285581f90 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 22 Feb 2018 16:16:27 +0100 Subject: [PATCH 089/242] Use EVM version in type checker. --- libsolidity/analysis/TypeChecker.h | 9 ++++++++- libsolidity/interface/CompilerStack.cpp | 2 +- test/libsolidity/Assembly.cpp | 13 ++++++++----- test/libsolidity/SolidityExpressionCompiler.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 5 +++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 16796b63f15b..2ba312327cca 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -22,6 +22,8 @@ #pragma once +#include + #include #include #include @@ -43,7 +45,10 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter): + m_evmVersion(_evmVersion), + m_errorReporter(_errorReporter) + {} /// Performs type checking on the given contract and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -132,6 +137,8 @@ class TypeChecker: private ASTConstVisitor ContractDefinition const* m_scope = nullptr; + EVMVersion m_evmVersion; + /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 2f7338253ba5..cb1ca3aa5700 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -205,7 +205,7 @@ bool CompilerStack::analyze() m_contracts[contract->fullyQualifiedName()].contract = contract; } - TypeChecker typeChecker(m_errorReporter); + TypeChecker typeChecker(m_evmVersion, m_errorReporter); for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 59af6d412f79..57a4f4e07587 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,11 +20,9 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include -#include -#include #include #include + #include #include #include @@ -33,6 +31,11 @@ #include #include +#include + +#include +#include + using namespace std; using namespace dev::eth; @@ -46,7 +49,7 @@ namespace test namespace { -eth::AssemblyItems compileContract(const string& _sourceCode) +eth::AssemblyItems compileContract(string const& _sourceCode) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -69,7 +72,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(errorReporter); + TypeChecker checker(EVMVersion{}, errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index e2a0c3cdb3ad..3a5aa941a343 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -132,7 +132,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(errorReporter); + TypeChecker typeChecker(EVMVersion{}, errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8c2d853ceaba..6849fd0722d9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3350,6 +3350,11 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; + m_compiler.setEVMVersion(EVMVersion{}); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } From dc317a44e031d45ebf745b47248bf06bc92d58bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 11:42:53 +0100 Subject: [PATCH 090/242] Provide EVM version to assembly analysis. --- libsolidity/analysis/ReferencesResolver.cpp | 3 +- libsolidity/analysis/TypeChecker.cpp | 1 + libsolidity/codegen/CompilerContext.cpp | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 32 +++++++++++++++------ libsolidity/inlineasm/AsmAnalysis.h | 5 +++- libsolidity/interface/AssemblyStack.cpp | 2 +- libsolidity/interface/AssemblyStack.h | 7 +++-- solc/CommandLineInterface.cpp | 31 ++++++++++---------- solc/CommandLineInterface.h | 2 +- test/ExecutionFramework.cpp | 8 +----- test/TestHelper.cpp | 16 ++++++++++- test/TestHelper.h | 5 +++- test/libjulia/Common.cpp | 9 +++++- test/libjulia/Parser.cpp | 7 ++++- test/libsolidity/InlineAssembly.cpp | 6 ++-- 15 files changed, 91 insertions(+), 44 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 985c44d0ab00..296a39c23bb3 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -278,8 +278,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) }; // Will be re-generated later with correct information + // We use the latest EVM version because we will re-run it anyway. assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2914472afe5f..04185b3864f8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -875,6 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, + m_evmVersion, assembly::AsmFlavour::Loose, identifierAccess ); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 0198a10798dd..ebf0213ad6a6 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -329,6 +329,7 @@ void CompilerContext::appendInlineAssembly( analyzerResult = assembly::AsmAnalyzer( analysisInfo, errorReporter, + m_evmVersion, assembly::AsmFlavour::Strict, identifierAccess.resolve ).analyze(*parserResult); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 1030523a6068..efa0410de9d2 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -533,19 +533,33 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location) { - static set futureInstructions{ - solidity::Instruction::CREATE2, - solidity::Instruction::RETURNDATACOPY, - solidity::Instruction::RETURNDATASIZE, - solidity::Instruction::STATICCALL - }; - if (futureInstructions.count(_instr)) + // We assume that returndatacopy, returndatasize and staticcall are either all available + // or all not available. + solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + + if (_instr == solidity::Instruction::CREATE2) m_errorReporter.warning( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available after " + - "the Metropolis hard fork. Before that it acts as an invalid instruction." + + "\" instruction is not supported by the VM version \"" + + "" + m_evmVersion.name() + + "\" you are currently compiling for. " + + "It will be interpreted as an invalid instruction on this VM." + ); + else if (( + _instr == solidity::Instruction::RETURNDATACOPY || + _instr == solidity::Instruction::RETURNDATASIZE || + _instr == solidity::Instruction::STATICCALL + ) && !m_evmVersion.supportsReturndata()) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available for Byzantium-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." ); static set experimentalInstructions{ diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 7a81dbf8bd95..867711c71a9e 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include @@ -54,9 +55,10 @@ class AsmAnalyzer: public boost::static_visitor explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, + EVMVersion _evmVersion, AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {} bool analyze(assembly::Block const& _block); @@ -97,6 +99,7 @@ class AsmAnalyzer: public boost::static_visitor std::set m_activeVariables; AsmAnalysisInfo& m_info; ErrorReporter& m_errorReporter; + EVMVersion m_evmVersion; AsmFlavour m_flavour = AsmFlavour::Loose; }; diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index c9e534c76d72..7a9fffbf299a 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann bool AssemblyStack::analyzeParsed() { m_analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language)); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language)); m_analysisSuccessful = analyzer.analyze(*m_parserResult); return m_analysisSuccessful; } diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 6ae7e8d1b9ba..720220ab2fd8 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -22,6 +22,8 @@ #pragma once #include +#include + #include #include @@ -54,8 +56,8 @@ class AssemblyStack enum class Language { JULIA, Assembly, StrictAssembly }; enum class Machine { EVM, EVM15, eWasm }; - explicit AssemblyStack(Language _language = Language::Assembly): - m_language(_language), m_errorReporter(m_errors) + explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly): + m_language(_language), m_evmVersion(_evmVersion), m_errorReporter(m_errors) {} /// @returns the scanner used during parsing @@ -82,6 +84,7 @@ class AssemblyStack bool analyzeParsed(); Language m_language = Language::Assembly; + EVMVersion m_evmVersion; std::shared_ptr m_scanner; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 04d6d1a8d905..8ccb04eeedfe 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -748,6 +748,19 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; + EVMVersion evmVersion; + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + boost::optional versionOption = EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return false; + } + evmVersion = *versionOption; + } + if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia)) { // switch to assembly mode @@ -771,7 +784,7 @@ bool CommandLineInterface::processInput() return false; } } - return assemble(inputLanguage, targetMachine); + return assemble(evmVersion, inputLanguage, targetMachine); } if (m_args.count(g_argLink)) { @@ -782,19 +795,6 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); - EVMVersion evmVersion; - if (m_args.count(g_strEVMVersion)) - { - string versionOptionStr = m_args[g_strEVMVersion].as(); - boost::optional versionOption = EVMVersion::fromString(versionOptionStr); - if (!versionOption) - { - cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; - return false; - } - evmVersion = *versionOption; - } - auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; SourceReferenceFormatter formatter(cerr, scannerFromSourceName); @@ -1081,6 +1081,7 @@ void CommandLineInterface::writeLinkedFiles() } bool CommandLineInterface::assemble( + EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine ) @@ -1089,7 +1090,7 @@ bool CommandLineInterface::assemble( map assemblyStacks; for (auto const& src: m_sourceCodes) { - auto& stack = assemblyStacks[src.first] = AssemblyStack(_language); + auto& stack = assemblyStacks[src.first] = AssemblyStack(_evmVersion, _language); try { if (!stack.parseAndAnalyze(src.first, src.second)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 4768c9d880ba..81117fdc1807 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -54,7 +54,7 @@ class CommandLineInterface bool link(); void writeLinkedFiles(); - bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); + bool assemble(EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); void outputCompilationResults(); diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index adf514e218dd..a24f78fbb19c 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -51,17 +51,11 @@ string getIPCSocketPath() ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), + m_evmVersion(dev::test::Options::get().evmVersion()), m_optimize(dev::test::Options::get().optimize), m_showMessages(dev::test::Options::get().showMessages), m_sender(m_rpc.account(0)) { - if (!dev::test::Options::get().evmVersion.empty()) - { - auto version = solidity::EVMVersion::fromString(dev::test::Options::get().evmVersion); - BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + dev::test::Options::get().evmVersion); - m_evmVersion = *version; - } - m_rpc.test_rewindToBlock(0); } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index fbf2dc90168c..e0d4423dc0af 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -47,7 +47,7 @@ Options::Options() optimize = true; else if (string(suite.argv[i]) == "--evm-version") { - evmVersion = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; + evmVersionString = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID"; ++i; } else if (string(suite.argv[i]) == "--show-messages") @@ -61,3 +61,17 @@ Options::Options() if (auto path = getenv("ETH_TEST_IPC")) ipcPath = path; } + +dev::solidity::EVMVersion Options::evmVersion() const +{ + if (!evmVersionString.empty()) + { + // We do this check as opposed to in the constructor because the BOOST_REQUIRE + // macros cannot yet be used in the constructor. + auto version = solidity::EVMVersion::fromString(evmVersionString); + BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + evmVersionString); + return *version; + } + else + return dev::solidity::EVMVersion(); +} diff --git a/test/TestHelper.h b/test/TestHelper.h index 69ac458a8fdc..8c2eec362ecf 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -36,14 +36,17 @@ struct Options: boost::noncopyable { std::string ipcPath; bool showMessages = false; - std::string evmVersion; bool optimize = false; bool disableIPC = false; bool disableSMT = false; + solidity::EVMVersion evmVersion() const; + static Options const& get(); private: + std::string evmVersionString; + Options(); }; diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 7053a68d1d00..d8cd20b6147c 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -21,6 +21,8 @@ #include +#include + #include #include @@ -61,7 +63,12 @@ pair, shared_ptr> dev::julia::test: { BOOST_REQUIRE(errorReporter.errors().empty()); auto analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, flavour); + assembly::AsmAnalyzer analyzer( + *analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + flavour + ); if (analyzer.analyze(*parserResult)) { BOOST_REQUIRE(errorReporter.errors().empty()); diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index ff9474c1eee9..6476c4d4589e 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -56,7 +56,12 @@ bool parse(string const& _source, ErrorReporter& errorReporter) if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer(analysisInfo, errorReporter, assembly::AsmFlavour::IULIA)).analyze(*parserResult); + return (assembly::AsmAnalyzer( + analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + assembly::AsmFlavour::IULIA + )).analyze(*parserResult); } } catch (FatalError const&) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index ea120657ad03..16ab611a211b 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -55,7 +55,7 @@ boost::optional parseAndReturnFirstError( AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - AssemblyStack stack(_language); + AssemblyStack stack(dev::test::Options::get().evmVersion(), _language); bool success = false; try { @@ -117,7 +117,7 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); if (_canWarn) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); @@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; - AssemblyStack stack; + AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); From 739533e9c7534cd3a0b321a3a6be1e041c91e4a2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 16:41:23 +0100 Subject: [PATCH 091/242] Activate byzantium for testing. --- test/RPCSession.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 69c75ceef34c..79ddf754dbf2 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -19,7 +19,11 @@ /// @file RPCSession.cpp /// Low-level IPC communication between the test framework and the Ethereum node. -#include "RPCSession.h" +#include + +#include + +#include #include @@ -215,6 +219,9 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector const& _accounts) { + string enableByzantium; + if (test::Options::get().evmVersion() == solidity::EVMVersion::byzantium()) + enableByzantium = "\"byzantiumForkBlock\": \"0x00\","; static string const c_configString = R"( { "sealEngine": "NoProof", @@ -224,6 +231,7 @@ void RPCSession::test_setChainParams(vector const& _accounts) "blockReward": "0x", "allowFutureBlocks": true, "homesteadForkBlock": "0x00", + )" + enableByzantium + R"( "EIP150ForkBlock": "0x00", "EIP158ForkBlock": "0x00" }, From 05cc5f22b204a0c389e1de4feaa44d33492dd053 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:20 +0100 Subject: [PATCH 092/242] Correctly set evm version in tests --- test/libsolidity/ASTJSON.cpp | 21 +++++++++++++-- test/libsolidity/AnalysisFramework.cpp | 3 +++ test/libsolidity/Assembly.cpp | 6 +++-- test/libsolidity/GasMeter.cpp | 1 + test/libsolidity/Imports.cpp | 27 +++++++++++++++++++ test/libsolidity/Metadata.cpp | 4 +++ test/libsolidity/SolidityABIJSON.cpp | 2 ++ .../SolidityExpressionCompiler.cpp | 4 +-- .../SolidityNameAndTypeResolution.cpp | 2 ++ test/libsolidity/SolidityNatspecJSON.cpp | 2 ++ test/libsolidity/ViewPureChecker.cpp | 2 ++ 11 files changed, 68 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index a165f7a996a1..9bf60b6477a3 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -20,12 +20,16 @@ * Tests for the json ast output. */ -#include -#include +#include + #include #include #include +#include + +#include + using namespace std; namespace dev @@ -41,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -52,6 +57,7 @@ BOOST_AUTO_TEST_CASE(source_location) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -66,6 +72,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier) { CompilerStack c; c.addSource("a", "contract C1 {} contract C2 is C1 {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -81,6 +88,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive) { CompilerStack c; c.addSource("a", "library L {} contract C { using L for uint; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -98,6 +106,7 @@ BOOST_AUTO_TEST_CASE(enum_value) { CompilerStack c; c.addSource("a", "contract C { enum E { A, B } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -115,6 +124,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -129,6 +139,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) { CompilerStack c; c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -145,6 +156,7 @@ BOOST_AUTO_TEST_CASE(event_definition) { CompilerStack c; c.addSource("a", "contract C { event E(); }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -159,6 +171,7 @@ BOOST_AUTO_TEST_CASE(array_type_name) { CompilerStack c; c.addSource("a", "contract C { uint[] i; }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -172,6 +185,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement) { CompilerStack c; c.addSource("a", "contract C { modifier M { _; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -185,6 +199,7 @@ BOOST_AUTO_TEST_CASE(non_utf8) { CompilerStack c; c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -204,6 +219,7 @@ BOOST_AUTO_TEST_CASE(function_type) "contract C { function f(function() external payable returns (uint) x) " "returns (function() external constant returns (uint)) {} }" ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 1; @@ -244,6 +260,7 @@ BOOST_AUTO_TEST_CASE(documentation) " /** Some comment on fn.*/ function fn() public {}" "}" ); + c.setEVMVersion(dev::test::Options::get().evmVersion()); c.parseAndAnalyze(); map sourceIndices; sourceIndices["a"] = 0; diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index a27e32228cf4..7c335a482f8d 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -46,6 +48,7 @@ AnalysisFramework::parseAnalyseAndReturnError( { m_compiler.reset(); m_compiler.addSource("", _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); if (!m_compiler.parse()) { BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors()); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 57a4f4e07587..aff610a485d0 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,6 +20,8 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ +#include + #include #include @@ -72,7 +74,7 @@ eth::AssemblyItems compileContract(string const& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(EVMVersion{}, errorReporter); + TypeChecker checker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); @@ -80,7 +82,7 @@ eth::AssemblyItems compileContract(string const& _sourceCode) for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - Compiler compiler; + Compiler compiler(dev::test::Options::get().evmVersion()); compiler.compileContract(*contract, map{}, bytes()); return compiler.runtimeAssemblyItems(); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 9d3409dd070f..105a0398702b 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -49,6 +49,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework m_compiler.reset(false); m_compiler.addSource("", "pragma solidity >=0.0;\n" + _sourceCode); m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index dc1174f4d879..bc81b3b1c258 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -44,6 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -52,6 +54,7 @@ BOOST_AUTO_TEST_CASE(regular_import) CompilerStack c; c.addSource("a", "contract C {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -60,6 +63,7 @@ BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee) CompilerStack c; c.addSource("a", "contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -69,6 +73,7 @@ BOOST_AUTO_TEST_CASE(import_is_transitive) c.addSource("a", "contract C { } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; pragma solidity >=0.0;"); c.addSource("c", "import \"b\"; contract D is C {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -77,6 +82,7 @@ BOOST_AUTO_TEST_CASE(circular_import) CompilerStack c; c.addSource("a", "import \"b\"; contract C { D d; } pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract D { C c; } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -86,6 +92,7 @@ BOOST_AUTO_TEST_CASE(relative_import) c.addSource("a", "import \"./dir/b\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("dir/b", "contract B {} pragma solidity >=0.0;"); c.addSource("dir/c", "import \"../a\"; contract C is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -94,6 +101,7 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -102,6 +110,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -111,6 +120,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -119,6 +129,7 @@ BOOST_AUTO_TEST_CASE(library_name_clash_with_contract) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -128,6 +139,7 @@ BOOST_AUTO_TEST_CASE(complex_import) c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;"); c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -136,14 +148,19 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import) CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); c.addSource("b", "import \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import \"a\" as A; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A as b} from \"a\"; contract b {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract A {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); c.addSource("b", "import {A} from \"a\"; contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -155,6 +172,7 @@ BOOST_AUTO_TEST_CASE(remappings) c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"); c.addSource("Tee/tee.sol", "contract Tee {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -166,6 +184,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings) c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"); c.addSource("s_1.4.7/s.sol", "contract SSeven {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -174,6 +193,7 @@ BOOST_AUTO_TEST_CASE(filename_with_period) CompilerStack c; c.addSource("a/a.sol", "import \".b.sol\"; contract A is B {} pragma solidity >=0.0;"); c.addSource("a/.b.sol", "contract B {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -185,6 +205,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;"); c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;"); c.addSource("vendor/foo_2.0.0/foo.sol", "contract Foo2 {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -196,6 +217,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); c.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); CompilerStack d; d.setRemappings(vector{"a/b:x=e", "a:x/y/z=d"}); @@ -203,6 +225,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); d.addSource("e/y/z/z.sol", "contract E {} pragma solidity >=0.0;"); + d.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(d.compile()); } @@ -212,6 +235,7 @@ BOOST_AUTO_TEST_CASE(shadowing_via_import) c.addSource("a", "library A {} pragma solidity >=0.0;"); c.addSource("b", "library A {} pragma solidity >=0.0;"); c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!c.compile()); } @@ -225,6 +249,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); size_t errorCount = 0; for (auto const& e: c.errors()) @@ -251,6 +276,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) contract C { } )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); auto numErrors = c.errors().size(); // Sometimes we get the prerelease warning, sometimes not. @@ -274,6 +300,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias) pragma solidity >=0.0; import {C as msg} from "B.sol"; )"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); auto numErrors = c.errors().size(); // Sometimes we get the prerelease warning, sometimes not. diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 47cf1d3d6a8b..f1edeeb72a24 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -72,6 +73,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) )"; CompilerStack compilerStack; compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; @@ -106,6 +108,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) } )"; compilerStack.addSource("B", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); @@ -144,6 +147,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) } )"; compilerStack.addSource("C", std::string(sourceCode)); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index e242508a81c0..0d471b32f879 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -44,6 +44,8 @@ class JSONInterfaceChecker { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); + m_compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 3a5aa941a343..44d3daffa9ea 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -132,7 +132,7 @@ bytes compileFirstExpression( if (ContractDefinition* contract = dynamic_cast(node.get())) { ErrorReporter errorReporter(errors); - TypeChecker typeChecker(EVMVersion{}, errorReporter); + TypeChecker typeChecker(dev::test::Options::get().evmVersion(), errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) @@ -141,7 +141,7 @@ bytes compileFirstExpression( FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.expression() != nullptr); - CompilerContext context; + CompilerContext context(dev::test::Options::get().evmVersion()); context.resetVisitedNodes(contract); context.setInheritanceHierarchy(inheritanceHierarchy); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 6849fd0722d9..a5ed4402daf1 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,6 +22,8 @@ #include +#include + #include #include diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index e8906bb95fcd..49a725e04f8d 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -47,6 +47,7 @@ class DocumentationChecker { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedDocumentation; @@ -67,6 +68,7 @@ class DocumentationChecker { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + m_compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(!m_compilerStack.parseAndAnalyze()); BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError)); } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 2599ca28affd..f6b2c62e9d32 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include From 1e26011d2cdb11b2df089fa97e2ab15ac329a45c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:42 +0100 Subject: [PATCH 093/242] Returndatasize and staticcall test fixes. --- .../libsolidity/SolidityNameAndTypeResolution.cpp | 15 ++++++--------- test/libsolidity/ViewPureChecker.cpp | 5 ++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a5ed4402daf1..f92cd47c97fa 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3352,11 +3352,6 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - m_compiler.setEVMVersion(EVMVersion{}); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } @@ -7088,11 +7083,13 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) char const* text = R"( contract c { function f() public { uint returndatasize; assembly { returndatasize }}} )"; - CHECK_ALLOW_MULTI(text, (std::vector>{ + vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - {Error::Type::Warning, "only available after the Metropolis"} - })); + {Error::Type::DeclarationError, "Unbalanced stack"} + }); + if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); + CHECK_ALLOW_MULTI(text, expectations); } BOOST_AUTO_TEST_CASE(create2_as_variable) diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index f6b2c62e9d32..35a0c7e5fa2c 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -425,7 +425,10 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - CHECK_WARNING(text, "only available after the Metropolis"); + if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible"); + else + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(assembly_jump) From 7b0272ccfbfbe547cea3fea5a941645b1da2cd27 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 23 Feb 2018 19:29:57 +0100 Subject: [PATCH 094/242] CREATE2 test fix. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f92cd47c97fa..e3cde5d658b2 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7099,7 +7099,7 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) )"; CHECK_ALLOW_MULTI(text, (std::vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "only available after the Metropolis"}, + {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, {Error::Type::DeclarationError, "Unbalanced stack"} })); } From 982476f99d085072d25b703a146a6d92cd280714 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:53:38 +0100 Subject: [PATCH 095/242] Add TangerineWhistle. --- libsolidity/interface/EVMVersion.h | 39 ++++++++++++------- solc/CommandLineInterface.cpp | 2 +- test/RPCSession.cpp | 16 ++++---- .../SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 4 ++ test/libsolidity/ViewPureChecker.cpp | 2 +- 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 1ddcd218bc0d..954a9f8f12f3 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -23,6 +23,7 @@ #include #include +#include namespace dev { @@ -33,28 +34,40 @@ namespace solidity * A version specifier of the EVM we want to compile to. * Defaults to the latest version. */ -class EVMVersion +class EVMVersion: + boost::less_than_comparable, + boost::equality_comparable { public: EVMVersion() {} static EVMVersion homestead() { return {Version::Homestead}; } + static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; } + static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion byzantium() { return {Version::Byzantium}; } static boost::optional fromString(std::string const& _version) { - if (_version == "homestead") - return homestead(); - else if (_version == "byzantium") - return byzantium(); - else - return {}; + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()}) + if (_version == v.name()) + return v; + return {}; } bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } - bool operator!=(EVMVersion const& _other) const { return !this->operator==(_other); } + bool operator<(EVMVersion const& _other) const { return m_version < _other.m_version; } - std::string name() const { return m_version == Version::Byzantium ? "byzantium" : "homestead"; } + std::string name() const + { + switch (m_version) + { + case Version::Byzantium: return "byzantium"; + case Version::TangerineWhistle: return "tangerineWhistle"; + case Version::SpuriousDragon: return "spuriousDragon"; + case Version::Homestead: return "homestead"; + } + return "INVALID"; + } /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. bool supportsReturndata() const { return *this >= byzantium(); } @@ -62,14 +75,10 @@ class EVMVersion /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). - bool canOverchargeGasForCall() const - { - // @TODO when exactly was this introduced? Was together with the call stack fix. - return m_version == Version::Byzantium; - } + bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, Byzantium }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium }; EVMVersion(Version _version): m_version(_version) {} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8ccb04eeedfe..caa564bc3729 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -539,7 +539,7 @@ Allowed options)", ( g_strEVMVersion.c_str(), po::value()->value_name("version"), - "Select desired EVM version. Either homestead or byzantium (default)." + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon or byzantium (default)." ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 79ddf754dbf2..54871057f06e 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -219,9 +219,13 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector const& _accounts) { - string enableByzantium; - if (test::Options::get().evmVersion() == solidity::EVMVersion::byzantium()) - enableByzantium = "\"byzantiumForkBlock\": \"0x00\","; + string forks; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::tangerineWhistle()) + forks += "\"EIP150ForkBlock\": \"0x00\",\n"; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::spuriousDragon()) + forks += "\"EIP158ForkBlock\": \"0x00\",\n"; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium()) + forks += "\"byzantiumForkBlock\": \"0x00\",\n"; static string const c_configString = R"( { "sealEngine": "NoProof", @@ -230,10 +234,8 @@ void RPCSession::test_setChainParams(vector const& _accounts) "maximumExtraDataSize": "0x1000000", "blockReward": "0x", "allowFutureBlocks": true, - "homesteadForkBlock": "0x00", - )" + enableByzantium + R"( - "EIP150ForkBlock": "0x00", - "EIP158ForkBlock": "0x00" + )" + forks + R"( + "homesteadForkBlock": "0x00" }, "genesis": { "author": "0000000000000010000000000000000000000000", diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e3cde5d658b2..7c2603353493 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7087,7 +7087,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, {Error::Type::DeclarationError, "Unbalanced stack"} }); - if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + if (!dev::test::Options::get().evmVersion().supportsReturndata()) expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); CHECK_ALLOW_MULTI(text, expectations); } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index df3ece7c0414..4c8918be88cd 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -746,6 +746,10 @@ BOOST_AUTO_TEST_CASE(evm_version) Json::Value result; result = compile(inputForVersion("\"evmVersion\": \"homestead\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"tangerineWhistle\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"tangerineWhistle\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"spuriousDragon\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos); result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); // test default diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 35a0c7e5fa2c..e6a5cfd06bdc 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) } } )"; - if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + if (!dev::test::Options::get().evmVersion().hasStaticCall()) CHECK_WARNING(text, "\"staticcall\" instruction is only available for Byzantium-compatible"); else CHECK_SUCCESS_NO_WARNINGS(text); From 7171ac0124b9a23be9f631b8d8191aae07a52516 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 14:52:50 +0100 Subject: [PATCH 096/242] Simplify error handling in tests.sh --- scripts/tests.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 69b2b338e33e..9c20815eb0d9 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -96,7 +96,6 @@ then progress="" fi -ERROR_CODE=0 # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer # and homestead / byzantium VM, # pointing to that IPC endpoint. for optimize in "" "--optimize" @@ -116,9 +115,8 @@ do fi set +e "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc - THIS_ERR=$? set -e - if [ $? -ne 0 ]; then ERROR_CODE=$?; fi + if [ $? -ne 0 ]; then exit $?; fi done done From 4ce0e7775d86076b2f1d0e1d1390f7a76636d257 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:51:12 +0100 Subject: [PATCH 097/242] Add constantinople. --- libsolidity/interface/EVMVersion.h | 8 +++++--- solc/CommandLineInterface.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 954a9f8f12f3..738bf2030e15 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -45,6 +45,7 @@ class EVMVersion: static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; } static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; } static EVMVersion byzantium() { return {Version::Byzantium}; } + static EVMVersion constantinople() { return {Version::Constantinople}; } static boost::optional fromString(std::string const& _version) { @@ -61,10 +62,11 @@ class EVMVersion: { switch (m_version) { - case Version::Byzantium: return "byzantium"; + case Version::Homestead: return "homestead"; case Version::TangerineWhistle: return "tangerineWhistle"; case Version::SpuriousDragon: return "spuriousDragon"; - case Version::Homestead: return "homestead"; + case Version::Byzantium: return "byzantium"; + case Version::Constantinople: return "constantinople"; } return "INVALID"; } @@ -78,7 +80,7 @@ class EVMVersion: bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople }; EVMVersion(Version _version): m_version(_version) {} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index caa564bc3729..fd079656d7d8 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -539,7 +539,7 @@ Allowed options)", ( g_strEVMVersion.c_str(), po::value()->value_name("version"), - "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon or byzantium (default)." + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium (default) or constantinople." ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( From 1246917e323eeae635ba7ed6a8a56a1995815bfd Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:53:51 +0100 Subject: [PATCH 098/242] Explain json-io setting. --- docs/using-the-compiler.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 66e3ac35eba9..df30b6b4b613 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -101,6 +101,7 @@ Input Description enabled: true, runs: 500 }, + evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople // Metadata settings (optional) metadata: { // Use only literal content and not URLs (false by default) From 7f8e5733391bf2a02a7d1a701591dedddf6341b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 18:54:19 +0100 Subject: [PATCH 099/242] Abort on the first failed end-to-end run. --- scripts/tests.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 9c20815eb0d9..60dae2e428f5 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -113,10 +113,7 @@ do log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt fi fi - set +e "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc - set -e - if [ $? -ne 0 ]; then exit $?; fi done done From 5a54cd5c708227ad6982b06de7b799ece5065917 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 28 Feb 2018 08:43:18 +0100 Subject: [PATCH 100/242] Only warn for shift instructions if not using constantinople --- libsolidity/inlineasm/AsmAnalysis.cpp | 18 +++++++++--------- libsolidity/interface/EVMVersion.h | 1 + test/libsolidity/InlineAssembly.cpp | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index efa0410de9d2..a7f764a5b925 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -561,19 +561,19 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio m_evmVersion.name() + "\", where it will be interpreted as an invalid instruction." ); - - static set experimentalInstructions{ - solidity::Instruction::SHL, - solidity::Instruction::SHR, - solidity::Instruction::SAR - }; - if (experimentalInstructions.count(_instr)) + else if (( + _instr == solidity::Instruction::SHL || + _instr == solidity::Instruction::SHR || + _instr == solidity::Instruction::SAR + ) && !m_evmVersion.hasBitwiseShifting()) m_errorReporter.warning( _location, "The \"" + boost::to_lower_copy(instructionInfo(_instr).name) - + "\" instruction is only available after " + - "the Constantinople hard fork. Before that it acts as an invalid instruction." + + "\" instruction is only available for Constantinople-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." ); if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 738bf2030e15..13c4ec94da9a 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -74,6 +74,7 @@ class EVMVersion: /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } + bool hasBitwiseShifting() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 16ab611a211b..a4dcc4d5fa86 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -783,9 +783,9 @@ BOOST_AUTO_TEST_CASE(shift) BOOST_AUTO_TEST_CASE(shift_constantinople_warning) { - CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork"); - CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork"); - CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs."); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs."); } BOOST_AUTO_TEST_CASE(jump_warning) From 2c086cb90b59116fc300f52c135c465c82db1b1e Mon Sep 17 00:00:00 2001 From: bernard peh Date: Fri, 2 Mar 2018 16:25:52 +1100 Subject: [PATCH 101/242] clarify 2300 gas stipend in fallback function section --- docs/contracts.rst | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 967eb2c84711..cfd5256a8b61 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -523,16 +523,9 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -In such a context, there is usually very little gas available to the function call (to be precise, 2300 gas), so it is important to make fallback functions as cheap as possible. Note that the gas required by a transaction (as opposed to an internal call) that invokes the fallback function is much higher, because each transaction charges an additional amount of 21000 gas or more for things like signature checking. +It is worth nothing that the "send" or "transfer" function only has a 2300 gas stipend. If the fallback function is called by another contract account using the send/transfer function (eg address.transfer(1 ether)), then the fallback function only has 2300 gas to spend, meaning that it cannot do much other than basic logging. -In particular, the following operations will consume more gas than the stipend provided to a fallback function: - -- Writing to storage -- Creating a contract -- Calling an external function which consumes a large amount of gas -- Sending Ether - -Please ensure you test your fallback function thoroughly to ensure the execution cost is less than 2300 gas before deploying a contract. +Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. .. note:: Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve From 454b470ceed799cb22ceaef6ea2b0bbea7165df9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 10:20:56 +0100 Subject: [PATCH 102/242] Suggest expressions of the form '0x1234 * 1 day' instead of hex numbers with unit denominations. --- libsolidity/analysis/TypeChecker.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 4ff0fb8f531b..cc7492ddec55 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2041,12 +2041,14 @@ void TypeChecker::endVisit(Literal const& _literal) if (v050) m_errorReporter.fatalTypeError( _literal.location(), - "Hexadecimal numbers cannot be used with unit denominations." + "Hexadecimal numbers cannot be used with unit denominations. " + "You can use an expression of the form '0x1234 * 1 day' instead." ); else m_errorReporter.warning( _literal.location(), - "Hexadecimal numbers with unit denominations are deprecated." + "Hexadecimal numbers with unit denominations are deprecated. " + "You can use an expression of the form '0x1234 * 1 day' instead." ); } if (!_literal.annotation().type) From 0f64648203b6b4631b42fcacf9eb71c3d94c921f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 10:36:04 +0100 Subject: [PATCH 103/242] Parser: Add test case for reserved keywords. --- test/libsolidity/SolidityParser.cpp | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index b7097d0f7e20..f03b30e133bc 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1164,6 +1164,36 @@ BOOST_AUTO_TEST_CASE(constant_is_keyword) CHECK_PARSE_ERROR(text, "Expected identifier"); } +BOOST_AUTO_TEST_CASE(keyword_is_reserved) +{ + auto keywords = { + "abstract", + "after", + "case", + "catch", + "default", + "final", + "in", + "inline", + "let", + "match", + "null", + "of", + "relocatable", + "static", + "switch", + "try", + "type", + "typeof" + }; + + for (const auto& keyword: keywords) + { + auto text = std::string("contract ") + keyword + " {}"; + CHECK_PARSE_ERROR(text.c_str(), "Expected identifier"); + } +} + BOOST_AUTO_TEST_CASE(var_array) { char const* text = R"( From 66ee9aa2f1d5af5dbabd6699e98935261d49558c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 2 Mar 2018 12:29:48 +0100 Subject: [PATCH 104/242] Use 0.4.21 pragma for documentation that uses "emit". --- docs/introduction-to-smart-contracts.rst | 2 +- docs/solidity-by-example.rst | 6 +++--- docs/structure-of-a-contract.rst | 2 +- docs/types.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 11e072925586..56f0fe3e54b6 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Coin { // The keyword "public" makes those variables diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 57556fa52dea..27fefd497b68 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -219,7 +219,7 @@ activate themselves. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract SimpleAuction { // Parameters of the auction. Times are either @@ -376,7 +376,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract BlindAuction { struct Bid { @@ -529,7 +529,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Purchase { uint public value; diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 4a0873df88d8..df40b1d018b2 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -86,7 +86,7 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event diff --git a/docs/types.rst b/docs/types.rst index 3611bc3e6ae5..e704687ee949 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity ^0.4.21; contract Oracle { struct Request { From cde4e3172be3fe87e4e229f28ad96b0844fa4bd4 Mon Sep 17 00:00:00 2001 From: bernard peh Date: Fri, 2 Mar 2018 23:40:17 +1100 Subject: [PATCH 105/242] change the language so that it doesn't sound like send and transfer is providing the stipend --- docs/contracts.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index cfd5256a8b61..3df722db9ed7 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -523,7 +523,12 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -It is worth nothing that the "send" or "transfer" function only has a 2300 gas stipend. If the fallback function is called by another contract account using the send/transfer function (eg address.transfer(1 ether)), then the fallback function only has 2300 gas to spend, meaning that it cannot do much other than basic logging. +In the worst case, the fallback function can only rely on 2300 gas being available (for example when send or transfer is used), leaving not much room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend: + +- Writing to storage +- Creating a contract +- Calling an external function which consumes a large amount of gas +- Sending Ether Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it. From c2730a48931aca8acb5d89ad9e946f16eb9463c3 Mon Sep 17 00:00:00 2001 From: Furkan Ayhan Date: Sun, 4 Mar 2018 13:23:07 +0300 Subject: [PATCH 106/242] Correct warning message in Pure Functions doc wrong commit that caused this: (https://github.com/ethereum/solidity/commit/64eaff64200d166bdd48f81bceefec9bc83db72f#diff-754689a291c0a19b500c31eb6c1d30c7R506) --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 3df722db9ed7..12b785d5bf13 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo } .. warning:: - Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state. + Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state. .. index:: ! fallback function, function;fallback From 5d74b862a390ccf4f2471fb8b9941314b4727c11 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 4 Mar 2018 14:41:27 +0100 Subject: [PATCH 107/242] This z3 option is necessary for good solving performance --- libsolidity/formal/Z3Interface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 769e6edb1f33..125da00d7cc2 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -28,6 +28,7 @@ using namespace dev::solidity::smt; Z3Interface::Z3Interface(): m_solver(m_context) { + z3::set_param("rewriter.pull_cheap_ite", true); } void Z3Interface::reset() From c633c0eacba12e940b12c6ff58b5c6c55dc0122c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 2 Mar 2018 17:58:27 +0100 Subject: [PATCH 108/242] Move msg.gas to global function gasleft(). Closes #2971. --- docs/contracts.rst | 2 +- docs/miscellaneous.rst | 3 +- docs/units-and-global-variables.rst | 3 +- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/ast/Types.cpp | 14 +++++--- libsolidity/ast/Types.h | 3 +- libsolidity/codegen/ExpressionCompiler.cpp | 7 +++- test/libsolidity/SolidityEndToEndTest.cpp | 19 +++++++++++ .../SolidityExpressionCompiler.cpp | 33 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 25 ++++++++++++++ test/libsolidity/ViewPureChecker.cpp | 1 + 11 files changed, 102 insertions(+), 9 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 967eb2c84711..5754b734dfe8 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be assigned from an expression which is a constant at compile time. Any expression that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or ``block.number``) or -execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions +execution data (``msg.value``) or make calls to external contracts are disallowed. Expressions that might have a side-effect on memory allocation are allowed, but those that might have a side-effect on other memory objects are not. The built-in functions ``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 70ed6201da26..7d3d058b94a5 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -315,8 +315,9 @@ Global Variables - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp +- ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas +- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``msg.sender`` (``address``): sender of the message (current call) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index cc4d44464a18..1b58b1e86a44 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -58,8 +58,9 @@ Block and Transaction Properties - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch +- ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas +- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``msg.sender`` (``address``): sender of the message (current call) - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - ``msg.value`` (``uint``): number of wei sent with the message diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index fd39d860d09d..34cb61d8c7ca 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -39,6 +39,7 @@ m_magicVariables(vector>{ make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), make_shared("ecrecover", make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), + make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), make_shared("keccak256", make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), make_shared("log0", make_shared(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), make_shared("log1", make_shared(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 771ae64393e0..bde0dd6ef7b9 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,8 +3000,10 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contract) const { + solAssert(_contract, ""); + const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: @@ -3014,13 +3016,17 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"gaslimit", make_shared(256)} }); case Kind::Message: - return MemberList::MemberMap({ + { + std::vector members = { {"sender", make_shared(160, IntegerType::Modifier::Address)}, - {"gas", make_shared(256)}, {"value", make_shared(256)}, {"data", make_shared(DataLocation::CallData)}, {"sig", make_shared(4)} - }); + }; + if (!v050) + members.emplace_back("gas", make_shared(256)); + return MemberList::MemberMap(members); + } case Kind::Transaction: return MemberList::MemberMap({ {"origin", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7985521e2b26..c20a025f39b4 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -902,7 +902,8 @@ class FunctionType: public Type ByteArrayPush, ///< .push() to a dynamically sized byte array in storage ObjectCreation, ///< array creation using new Assert, ///< assert() - Require ///< require() + Require, ///< require() + GasLeft ///< gasleft() }; virtual Category category() const override { return Category::Function; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 619205922c04..d35993675fba 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -906,6 +906,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << success; break; } + case FunctionType::Kind::GasLeft: + m_context << Instruction::GAS; + break; default: solAssert(false, "Invalid function type."); } @@ -921,6 +924,8 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { + const bool v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); + CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. ASTString const& member = _memberAccess.memberName(); @@ -1135,7 +1140,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (member == "gas") + else if (!v050 && member == "gas") m_context << Instruction::GAS; else if (member == "gasprice") m_context << Instruction::GASPRICE; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c352a2c22518..e61afb0ea76e 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2481,6 +2481,25 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* sourceCode = R"( + contract test { + function getGasLeft() public returns (uint256 val) { return msg.gas; } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); + + sourceCode = R"( + contract test { + function getGasLeft() public returns (uint256 val) { return gasleft(); } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); +} + BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index e2a0c3cdb3ad..83129ba1ddf1 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -515,6 +515,39 @@ BOOST_AUTO_TEST_CASE(blockhash) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return msg.gas; + } + } + )"; + bytes code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared("msg", make_shared(MagicType::Kind::Message))} + ); + + bytes expectation({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); + + sourceCode = R"( + contract test { + function f() returns (uint256 val) { + return gasleft(); + } + } + )"; + code = compileFirstExpression( + sourceCode, {}, {}, + {make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))} + ); + + expectation = bytes({byte(Instruction::GAS)}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index eeefe818e48d..469211d31844 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7409,6 +7409,31 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); } +BOOST_AUTO_TEST_CASE(gas_left) +{ + char const* text = R"( + contract C { + function f() public returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_SUCCESS(text); + + text = R"( + contract C { + function f() public returns (uint256 val) { return gasleft(); } + } + )"; + CHECK_SUCCESS(text); + + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() public returns (uint256 val) { return msg.gas; } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); +} + BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 2599ca28affd..609a01545906 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -109,6 +109,7 @@ BOOST_AUTO_TEST_CASE(environment_access) "block.difficulty", "block.number", "block.gaslimit", + "gasleft()", "msg.gas", "msg.value", "msg.sender", From 6ec4517929e8c0eca022f4771ba217db5d80beed Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 1 Mar 2018 12:06:36 +0100 Subject: [PATCH 109/242] Use EVM version in gas meter and optimizer. --- libevmasm/Assembly.cpp | 4 +- libevmasm/Assembly.h | 5 +- libevmasm/ConstantOptimiser.cpp | 11 +- libevmasm/ConstantOptimiser.h | 4 + libevmasm/GasMeter.cpp | 32 ++- libevmasm/GasMeter.h | 44 +++- libevmasm/PathGasMeter.cpp | 6 +- libevmasm/PathGasMeter.h | 8 +- liblll/Compiler.cpp | 17 +- liblll/Compiler.h | 9 +- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 4 +- libsolidity/codegen/ContractCompiler.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 9 +- libsolidity/interface/GasEstimator.cpp | 12 +- libsolidity/interface/GasEstimator.h | 22 +- lllc/main.cpp | 4 +- solc/CommandLineInterface.cpp | 13 +- solc/CommandLineInterface.h | 5 +- test/contracts/LLL_ENS.cpp | 2 +- test/contracts/LLL_ERC20.cpp | 2 +- test/fuzzer.cpp | 1 + test/libevmasm/Optimiser.cpp | 4 +- test/liblll/Compiler.cpp | 17 +- test/liblll/EndToEndTest.cpp | 276 +++++++++++++-------- test/liblll/ExecutionFramework.h | 2 +- test/libsolidity/GasMeter.cpp | 10 +- 28 files changed, 328 insertions(+), 201 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index b9fedf26c40e..bd4ebf59a506 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -353,7 +353,7 @@ void Assembly::injectStart(AssemblyItem const& _i) m_items.insert(m_items.begin(), _i); } -Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) +Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; settings.isCreation = _isCreation; @@ -365,6 +365,7 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) settings.runCSE = true; settings.runConstantOptimiser = true; } + settings.evmVersion = _evmVersion; settings.expectedExecutionsPerDeployment = _runs; optimise(settings); return *this; @@ -482,6 +483,7 @@ map Assembly::optimiseInternal( ConstantOptimisationMethod::optimiseConstants( _settings.isCreation, _settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment, + _settings.evmVersion, *this, m_items ); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 885192e49808..367c6daad2c4 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include @@ -107,6 +109,7 @@ class Assembly bool runDeduplicate = false; bool runCSE = false; bool runConstantOptimiser = false; + solidity::EVMVersion evmVersion; /// This specifies an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage. size_t expectedExecutionsPerDeployment = 200; @@ -120,7 +123,7 @@ class Assembly /// @a _runs specifes an estimate on how often each opcode in this assembly will be executed, /// i.e. use a small value to optimise for size and a large value to optimise for runtime. /// If @a _enable is not set, will perform some simple peephole optimizations. - Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); + Assembly& optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200); /// Create a text representation of the assembly. std::string assemblyString( diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 2efd2dc9c4e6..d0b6843ca597 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -29,6 +29,7 @@ using namespace dev::eth; unsigned ConstantOptimisationMethod::optimiseConstants( bool _isCreation, size_t _runs, + solidity::EVMVersion _evmVersion, Assembly& _assembly, AssemblyItems& _items ) @@ -48,6 +49,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants( params.multiplicity = it.second; params.isCreation = _isCreation; params.runs = _runs; + params.evmVersion = _evmVersion; LiteralMethod lit(params, item.data()); bigint literalGas = lit.gasNeeded(); CodeCopyMethod copy(params, item.data()); @@ -80,7 +82,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) if (item.type() == Push) gas += GasMeter::runGas(Instruction::PUSH1); else if (item.type() == Operation) - gas += GasMeter::runGas(item.instruction()); + { + if (item.instruction() == Instruction::EXP) + gas += GasCosts::expGas; + else + gas += GasMeter::runGas(item.instruction()); + } return gas; } @@ -286,7 +293,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP); return combineGas( - simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas), + simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), // Data gas for routine: Some bytes are zero, but we ignore them. bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), 0 diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index c450b0b4c126..9b60b26b259d 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -23,6 +23,8 @@ #include +#include + #include #include #include @@ -50,6 +52,7 @@ class ConstantOptimisationMethod static unsigned optimiseConstants( bool _isCreation, size_t _runs, + solidity::EVMVersion _evmVersion, Assembly& _assembly, AssemblyItems& _items ); @@ -59,6 +62,7 @@ class ConstantOptimisationMethod bool isCreation; ///< Whether this is called during contract creation or runtime. size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract. size_t multiplicity; ///< Number of times the constant appears in the code. + solidity::EVMVersion evmVersion; ///< Version of the EVM }; explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 543f1cbc3e32..caa06fc07947 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -61,7 +61,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Operation: { ExpressionClasses& classes = m_state->expressionClasses(); - gas = runGas(_item.instruction()); switch (_item.instruction()) { case Instruction::SSTORE: @@ -72,26 +71,29 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ m_state->storageContent().count(slot) && classes.knownNonZero(m_state->storageContent().at(slot)) )) - gas += GasCosts::sstoreResetGas; //@todo take refunds into account + gas = GasCosts::sstoreResetGas; //@todo take refunds into account else - gas += GasCosts::sstoreSetGas; + gas = GasCosts::sstoreSetGas; break; } case Instruction::SLOAD: - gas += GasCosts::sloadGas; + gas = GasCosts::sloadGas(m_evmVersion); break; case Instruction::RETURN: case Instruction::REVERT: + gas = runGas(_item.instruction()); gas += memoryGas(0, -1); break; case Instruction::MLOAD: case Instruction::MSTORE: + gas = runGas(_item.instruction()); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(32)) })); break; case Instruction::MSTORE8: + gas = runGas(_item.instruction()); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(1)) @@ -105,10 +107,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::CALLDATACOPY: case Instruction::CODECOPY: case Instruction::RETURNDATACOPY: + gas = runGas(_item.instruction()); gas += memoryGas(0, -2); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; + case Instruction::EXTCODESIZE: + gas = GasCosts::extCodeGas(m_evmVersion); + break; case Instruction::EXTCODECOPY: + gas = GasCosts::extCodeGas(m_evmVersion); gas += memoryGas(-1, -3); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3)); break; @@ -137,7 +144,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas = GasConsumption::infinite(); else { - gas = GasCosts::callGas; + gas = GasCosts::callGas(m_evmVersion); if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) gas += (*value); else @@ -155,7 +162,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; } case Instruction::SELFDESTRUCT: - gas = GasCosts::selfdestructGas; + gas = GasCosts::selfdestructGas(m_evmVersion); gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists. break; case Instruction::CREATE: @@ -172,11 +179,15 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::EXP: gas = GasCosts::expGas; if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) - gas += GasCosts::expByteGas * (32 - (h256(*value).firstBitSet() / 8)); + gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8)); else - gas += GasCosts::expByteGas * 32; + gas += GasCosts::expByteGas(m_evmVersion) * 32; + break; + case Instruction::BALANCE: + gas = GasCosts::balanceGas(m_evmVersion); break; default: + gas = runGas(_item.instruction()); break; } break; @@ -241,12 +252,9 @@ unsigned GasMeter::runGas(Instruction _instruction) case Tier::Mid: return GasCosts::tier4Gas; case Tier::High: return GasCosts::tier5Gas; case Tier::Ext: return GasCosts::tier6Gas; - case Tier::Special: return GasCosts::tier7Gas; - case Tier::ExtCode: return GasCosts::extCodeGas; - case Tier::Balance: return GasCosts::balanceGas; default: break; } - assertThrow(false, OptimizerException, "Invalid gas tier."); + assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name); return 0; } diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 2c3ecf5a7c44..b131802f55b2 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -21,11 +21,14 @@ #pragma once -#include -#include #include #include +#include + +#include +#include + namespace dev { namespace eth @@ -44,13 +47,25 @@ namespace GasCosts static unsigned const tier5Gas = 10; static unsigned const tier6Gas = 20; static unsigned const tier7Gas = 0; - static unsigned const extCodeGas = 700; - static unsigned const balanceGas = 400; + inline unsigned extCodeGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 20; + } + inline unsigned balanceGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 400 : 20; + } static unsigned const expGas = 10; - static unsigned const expByteGas = 50; + inline unsigned expByteGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::spuriousDragon() ? 50 : 10; + } static unsigned const keccak256Gas = 30; static unsigned const keccak256WordGas = 6; - static unsigned const sloadGas = 200; + inline unsigned sloadGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 200 : 50; + } static unsigned const sstoreSetGas = 20000; static unsigned const sstoreResetGas = 5000; static unsigned const sstoreRefundGas = 15000; @@ -59,11 +74,17 @@ namespace GasCosts static unsigned const logDataGas = 8; static unsigned const logTopicGas = 375; static unsigned const createGas = 32000; - static unsigned const callGas = 700; + inline unsigned callGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 700 : 40; + } static unsigned const callStipend = 2300; static unsigned const callValueTransferGas = 9000; static unsigned const callNewAccountGas = 25000; - static unsigned const selfdestructGas = 5000; + inline unsigned selfdestructGas(EVMVersion _evmVersion) + { + return _evmVersion >= EVMVersion::tangerineWhistle() ? 5000 : 0; + } static unsigned const selfdestructRefundGas = 24000; static unsigned const memoryGas = 3; static unsigned const quadCoeffDiv = 512; @@ -100,8 +121,8 @@ class GasMeter }; /// Constructs a new gas meter given the current state. - explicit GasMeter(std::shared_ptr const& _state, u256 const& _largestMemoryAccess = 0): - m_state(_state), m_largestMemoryAccess(_largestMemoryAccess) {} + GasMeter(std::shared_ptr const& _state, solidity::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): + m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {} /// @returns an upper bound on the gas consumed by the given instruction and updates /// the state. @@ -110,6 +131,8 @@ class GasMeter u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; } + /// @returns gas costs for simple instructions with constant gas costs (that do not + /// change with EVM versions) static unsigned runGas(Instruction _instruction); private: @@ -123,6 +146,7 @@ class GasMeter GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); std::shared_ptr m_state; + EVMVersion m_evmVersion; /// Largest point where memory was accessed since the creation of this object. u256 m_largestMemoryAccess; }; diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp index c56e2f8ba2fc..3fe682b7f993 100644 --- a/libevmasm/PathGasMeter.cpp +++ b/libevmasm/PathGasMeter.cpp @@ -27,8 +27,8 @@ using namespace std; using namespace dev; using namespace dev::eth; -PathGasMeter::PathGasMeter(AssemblyItems const& _items): - m_items(_items) +PathGasMeter::PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion): + m_items(_items), m_evmVersion(_evmVersion) { for (size_t i = 0; i < m_items.size(); ++i) if (m_items[i].type() == Tag) @@ -59,7 +59,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem() m_queue.pop_back(); shared_ptr state = path->state; - GasMeter meter(state, path->largestMemoryAccess); + GasMeter meter(state, m_evmVersion, path->largestMemoryAccess); ExpressionClasses& classes = state->expressionClasses(); GasMeter::GasConsumption gas = path->gas; size_t index = path->index; diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 4826eac2a453..2527d7fb93f3 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -21,10 +21,13 @@ #pragma once +#include + +#include + #include #include #include -#include namespace dev { @@ -50,7 +53,7 @@ struct GasPath class PathGasMeter { public: - explicit PathGasMeter(AssemblyItems const& _items); + explicit PathGasMeter(AssemblyItems const& _items, solidity::EVMVersion _evmVersion); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr const& _state); @@ -60,6 +63,7 @@ class PathGasMeter std::vector> m_queue; std::map m_tagPositions; AssemblyItems const& m_items; + solidity::EVMVersion m_evmVersion; }; } diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 1638f69e3388..f2c1b0be79e0 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -19,17 +19,16 @@ * @date 2014 */ -#include "Compiler.h" -#include "Parser.h" -#include "CompilerState.h" -#include "CodeFragment.h" +#include +#include +#include +#include using namespace std; using namespace dev; using namespace dev::eth; - -bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _errors, ReadCallback const& _readFile) +bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector* _errors, dev::eth::ReadCallback const& _readFile) { try { @@ -37,7 +36,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _error cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true); + assembly = assembly.optimise(true, _evmVersion); bytes ret = assembly.assemble().bytecode; for (auto i: cs.treesToKill) killBigints(i); @@ -67,7 +66,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector* _error return bytes(); } -std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::vector* _errors, ReadCallback const& _readFile) +std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector* _errors, ReadCallback const& _readFile) { try { @@ -75,7 +74,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v cs.populateStandard(); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); if (_opt) - assembly = assembly.optimise(true); + assembly = assembly.optimise(true, _evmVersion); string ret = assembly.assemblyString(); for (auto i: cs.treesToKill) killBigints(i); diff --git a/liblll/Compiler.h b/liblll/Compiler.h index c3395b667c4b..06440c17a8ba 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -21,9 +21,12 @@ #pragma once +#include + +#include + #include #include -#include namespace dev { @@ -33,8 +36,8 @@ namespace eth using ReadCallback = std::function; std::string parseLLL(std::string const& _src); -std::string compileLLLToAsm(std::string const& _src, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); -bytes compileLLL(std::string const& _src, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +std::string compileLLLToAsm(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); +bytes compileLLL(std::string const& _src, solidity::EVMVersion _evmVersion, bool _opt = true, std::vector* _errors = nullptr, ReadCallback const& _readFile = ReadCallback()); } } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index e6b2484a358d..cf62668331e9 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -209,7 +209,7 @@ class CompilerContext void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } /// Run optimisation step. - void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } + void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. CompilerContext* runtimeContext() { return m_runtimeContext; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebb718a58687..5a9498f0e056 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1059,7 +1059,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); } -eth::AssemblyPointer ContractCompiler::cloneRuntime() +eth::AssemblyPointer ContractCompiler::cloneRuntime() const { eth::Assembly a; a << Instruction::CALLDATASIZE; @@ -1070,7 +1070,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() // this is the address which has to be substituted by the linker. //@todo implement as special "marker" AssemblyItem. a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB; + a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB; a << Instruction::DELEGATECALL; //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << Instruction::ISZERO; diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 18f319670650..8559ea587b22 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -125,7 +125,7 @@ class ContractCompiler: private ASTConstVisitor void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); /// @returns the runtime assembly for clone contracts. - static eth::AssemblyPointer cloneRuntime(); + eth::AssemblyPointer cloneRuntime() const; bool const m_optimise; /// Pointer to the runtime compiler in case this is a creation compiler. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 619205922c04..12881d637d06 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1756,7 +1756,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. - u256 gasNeededByCaller = eth::GasCosts::callGas + 10; + u256 gasNeededByCaller = eth::GasCosts::callGas(m_context.evmVersion()) + 10; if (_functionType.valueSet()) gasNeededByCaller += eth::GasCosts::callValueTransferGas; if (!existenceChecked) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index cb1ca3aa5700..eacfca9c8fae 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -959,11 +959,12 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const return Json::Value(); using Gas = GasEstimator::GasConsumption; + GasEstimator gasEstimator(m_evmVersion); Json::Value output(Json::objectValue); if (eth::AssemblyItems const* items = assemblyItems(_contractName)) { - Gas executionGas = GasEstimator::functionalEstimation(*items); + Gas executionGas = gasEstimator.functionalEstimation(*items); u256 bytecodeSize(runtimeObject(_contractName).bytecode.size()); Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas; @@ -984,14 +985,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const for (auto it: contract.interfaceFunctions()) { string sig = it.second->externalSignature(); - externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig)); + externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig)); } if (contract.fallbackFunction()) /// This needs to be set to an invalid signature in order to trigger the fallback, /// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound. /// An empty string ("") would work to trigger the shortcut only. - externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID")); + externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID")); if (!externalFunctions.empty()) output["external"] = externalFunctions; @@ -1007,7 +1008,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const size_t entry = functionEntryPoint(_contractName, *it); GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite(); if (entry > 0) - gas = GasEstimator::functionalEstimation(*items, entry, *it); + gas = gasEstimator.functionalEstimation(*items, entry, *it); /// TODO: This could move into a method shared with externalSignature() FunctionType type(*it); diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 22cc0266d447..2139395f2f2b 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -40,7 +40,7 @@ using namespace dev::solidity; GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation( AssemblyItems const& _items, vector const& _ast -) +) const { solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); map particularCosts; @@ -49,7 +49,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio for (BasicBlock const& block: cfg.optimisedBlocks()) { solAssert(!!block.startState, ""); - GasMeter meter(block.startState->copy()); + GasMeter meter(block.startState->copy(), m_evmVersion); auto const end = _items.begin() + block.end; for (auto iter = _items.begin() + block.begin; iter != end; ++iter) particularCosts[iter->location()] += meter.estimateMax(*iter); @@ -127,7 +127,7 @@ map GasEstimator::breakToStatementLeve GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, string const& _signature -) +) const { auto state = make_shared(); @@ -144,7 +144,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( }); } - PathGasMeter meter(_items); + PathGasMeter meter(_items, m_evmVersion); return meter.estimateMax(0, state); } @@ -152,7 +152,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, size_t const& _offset, FunctionDefinition const& _function -) +) const { auto state = make_shared(); @@ -167,7 +167,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( if (parametersSize > 0) state->feedItem(swapInstruction(parametersSize)); - return PathGasMeter(_items).estimateMax(_offset, state); + return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state); } set GasEstimator::finestNodesAtLocation( diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index bf63df96d87f..ea94d9888481 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -22,11 +22,14 @@ #pragma once +#include + +#include +#include + #include #include #include -#include -#include namespace dev { @@ -44,13 +47,15 @@ struct GasEstimator using ASTGasConsumptionSelfAccumulated = std::map>; + explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} + /// Estimates the gas consumption for every assembly item in the given assembly and stores /// it by source location. /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs. - static ASTGasConsumptionSelfAccumulated structuralEstimation( + ASTGasConsumptionSelfAccumulated structuralEstimation( eth::AssemblyItems const& _items, std::vector const& _ast - ); + ) const; /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that /// the following source locations are part of the mapping: /// 1. source locations of statements that do not contain other statements @@ -62,23 +67,24 @@ struct GasEstimator /// @returns the estimated gas consumption by the (public or external) function with the /// given signature. If no signature is given, estimates the maximum gas usage. - static GasConsumption functionalEstimation( + GasConsumption functionalEstimation( eth::AssemblyItems const& _items, std::string const& _signature = "" - ); + ) const; /// @returns the estimated gas consumption by the given function which starts at the given /// offset into the list of assembly items. /// @note this does not work correctly for recursive functions. - static GasConsumption functionalEstimation( + GasConsumption functionalEstimation( eth::AssemblyItems const& _items, size_t const& _offset, FunctionDefinition const& _function - ); + ) const; private: /// @returns the set of AST nodes which are the finest nodes at their location. static std::set finestNodesAtLocation(std::vector const& _roots); + EVMVersion m_evmVersion; }; } diff --git a/lllc/main.cpp b/lllc/main.cpp index 5679bc2b0525..0ca3ff138cb3 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -133,7 +133,7 @@ int main(int argc, char** argv) } else if (mode == Binary || mode == Hex) { - auto bs = compileLLL(src, optimise ? true : false, &errors, readFileAsString); + auto bs = compileLLL(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString); if (mode == Hex) cout << toHex(bs) << endl; else if (mode == Binary) @@ -145,7 +145,7 @@ int main(int argc, char** argv) } else if (mode == Assembly) { - cout << compileLLLToAsm(src, optimise ? true : false, &errors, readFileAsString) << endl; + cout << compileLLLToAsm(src, EVMVersion{}, optimise ? true : false, &errors, readFileAsString) << endl; } for (auto const& i: errors) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fd079656d7d8..d3d234c343c1 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -748,7 +748,6 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; - EVMVersion evmVersion; if (m_args.count(g_strEVMVersion)) { string versionOptionStr = m_args[g_strEVMVersion].as(); @@ -758,7 +757,7 @@ bool CommandLineInterface::processInput() cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; return false; } - evmVersion = *versionOption; + m_evmVersion = *versionOption; } if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia)) @@ -784,7 +783,7 @@ bool CommandLineInterface::processInput() return false; } } - return assemble(evmVersion, inputLanguage, targetMachine); + return assemble(inputLanguage, targetMachine); } if (m_args.count(g_argLink)) { @@ -808,8 +807,7 @@ bool CommandLineInterface::processInput() m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); - if (m_args.count(g_strEVMVersion)) - m_compiler->setEVMVersion(evmVersion); + m_compiler->setEVMVersion(m_evmVersion); // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count(g_argOptimize) > 0; unsigned runs = m_args[g_argOptimizeRuns].as(); @@ -968,7 +966,7 @@ void CommandLineInterface::handleAst(string const& _argStr) // FIXME: shouldn't this be done for every contract? if (m_compiler->runtimeAssemblyItems(m_compiler->lastContractName())) gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), + GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), asts ); @@ -1081,7 +1079,6 @@ void CommandLineInterface::writeLinkedFiles() } bool CommandLineInterface::assemble( - EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine ) @@ -1090,7 +1087,7 @@ bool CommandLineInterface::assemble( map assemblyStacks; for (auto const& src: m_sourceCodes) { - auto& stack = assemblyStacks[src.first] = AssemblyStack(_evmVersion, _language); + auto& stack = assemblyStacks[src.first] = AssemblyStack(m_evmVersion, _language); try { if (!stack.parseAndAnalyze(src.first, src.second)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 81117fdc1807..303023fcbe41 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -54,7 +55,7 @@ class CommandLineInterface bool link(); void writeLinkedFiles(); - bool assemble(EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); + bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); void outputCompilationResults(); @@ -102,6 +103,8 @@ class CommandLineInterface std::map m_libraries; /// Solidity compiler stack std::unique_ptr m_compiler; + /// EVM version to use + EVMVersion m_evmVersion; }; } diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp index c5fe8a825b3d..028d58c87193 100644 --- a/test/contracts/LLL_ENS.cpp +++ b/test/contracts/LLL_ENS.cpp @@ -345,7 +345,7 @@ class LLLENSTestFramework: public LLLExecutionFramework if (!s_compiledEns) { vector errors; - s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().optimize, &errors))); + s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledEns, true); diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp index 25665d640a10..60b43e4f92f2 100644 --- a/test/contracts/LLL_ERC20.cpp +++ b/test/contracts/LLL_ERC20.cpp @@ -396,7 +396,7 @@ class LLLERC20TestFramework: public LLLExecutionFramework if (!s_compiledErc20) { vector errors; - s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().optimize, &errors))); + s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledErc20, true); diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 45738baab033..71f38b67340a 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -76,6 +76,7 @@ void testConstantOptimizer() ConstantOptimisationMethod::optimiseConstants( isCreation, runs, + EVMVersion{}, assembly, const_cast(assembly.items()) ); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 0ab95b080d08..e6abcb53169a 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -20,6 +20,8 @@ * Tests for the Solidity optimizer. */ +#include + #include #include #include @@ -916,7 +918,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) main.append(t1.toSubAssemblyTag(subId)); main.append(u256(8)); - main.optimise(true); + main.optimise(true, dev::test::Options::get().evmVersion()); AssemblyItems expectationMain{ AssemblyItem(PushSubSize, 0), diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index ace97e15c893..6c6eae3f96bd 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -20,11 +20,16 @@ * Unit tests for the LLL compiler. */ +#include + +#include + +#include + +#include + #include #include -#include -#include -#include using namespace std; @@ -41,7 +46,7 @@ namespace bool successCompile(string const& _sourceCode) { vector errors; - bytes bytecode = eth::compileLLL(_sourceCode, false, &errors); + bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), false, &errors); if (!errors.empty()) return false; if (bytecode.empty()) @@ -353,7 +358,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) for (size_t i = 0; i < opcodes_bytecode.size(); i++) { vector errors; - bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); @@ -641,7 +646,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) for (size_t i = 0; i < opcodes_bytecode.size(); i++) { vector errors; - bytes code = eth::compileLLL(opcodes_lll[i], false, &errors); + bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 1a5bb4906260..e5e70cf8cb70 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -20,10 +20,13 @@ * End to end tests for LLL. */ +#include +#include + +#include + #include #include -#include -#include using namespace std; @@ -583,24 +586,34 @@ BOOST_AUTO_TEST_CASE(allgas) BOOST_AUTO_TEST_CASE(send_two_args) { - char const* sourceCode = R"( - (returnlll - (send 0xdead 42)) - )"; - compileAndRun(sourceCode); - callFallbackWithValue(42); - BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + // "send" does not retain enough gas to be able to pay for account creation. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (send 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + } } BOOST_AUTO_TEST_CASE(send_three_args) { - char const* sourceCode = R"( - (returnlll - (send allgas 0xdead 42)) - )"; - compileAndRun(sourceCode); - callFallbackWithValue(42); - BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + // "send" does not retain enough gas to be able to pay for account creation. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (send allgas 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); + } } // Regression test for edge case that previously failed @@ -708,56 +721,76 @@ BOOST_AUTO_TEST_CASE(msg_four_args) BOOST_AUTO_TEST_CASE(msg_three_args) { - char const* sourceCode = R"( - (returnlll - (seq - (when (= 0 (calldatasize)) - (return (msg (address) 42 0xff))) - (return (callvalue)))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + // "msg" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 42 0xff))) + (return (callvalue)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(msg_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (when (= 0 (calldatasize)) - (return (msg (address) 0xff))) - (return 42))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + // "msg" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 0xff))) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(create_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (call allgas - (create (returnlll (return 42))) - 0 0 0 0x00 0x20) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + // "call" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create (returnlll (return 42))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(create_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (call allgas - (create 42 (returnlll (return (balance (address))))) - 0 0 0 0x00 0x20) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + // "call" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create 42 (returnlll (return (balance (address))))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); + } } BOOST_AUTO_TEST_CASE(sha3_two_args) @@ -822,77 +855,102 @@ BOOST_AUTO_TEST_CASE(makeperm) // Covers makeperm (implicit), permcount and perm BOOST_AUTO_TEST_CASE(ecrecover) { - char const* sourceCode = R"( - (returnlll - (return - (ecrecover - ; Hash of 'hello world' - 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad - ; v = 1 + 27 - 0x1c - ; r - 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215 - ; s - 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af"))); + // "ecrecover" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (return + (ecrecover + ; Hash of 'hello world' + 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad + ; v = 1 + 27 + 0x1c + ; r + 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215 + ; s + 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af"))); + } } BOOST_AUTO_TEST_CASE(sha256_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") - (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") - (sha256 0x20 0x40) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429"))); + // "sha256" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (sha256 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429"))); + } } BOOST_AUTO_TEST_CASE(ripemd160_two_args) { - char const* sourceCode = R"( - (returnlll - (seq - (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") - (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") - (ripemd160 0x20 0x40) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da"))); + // "ripemd160" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (ripemd160 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da"))); + } } BOOST_AUTO_TEST_CASE(sha256_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6"))); + // "sha256" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6"))); + } } BOOST_AUTO_TEST_CASE(ripemd160_one_arg) { - char const* sourceCode = R"( - (returnlll - (seq - (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) - (return 0x00 0x20))) - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callFallback() == encodeArgs( - fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546"))); + // "ripemd160" does not retain enough gas. + // Disabling for non-tangerineWhistle VMs. + if (dev::test::Options::get().evmVersion().canOverchargeGasForCall()) + { + char const* sourceCode = R"( + (returnlll + (seq + (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546"))); + } } BOOST_AUTO_TEST_CASE(wei_szabo_finney_ether) diff --git a/test/liblll/ExecutionFramework.h b/test/liblll/ExecutionFramework.h index 58e1f0addc68..ae5cd988cac3 100644 --- a/test/liblll/ExecutionFramework.h +++ b/test/liblll/ExecutionFramework.h @@ -56,7 +56,7 @@ class LLLExecutionFramework: public ExecutionFramework BOOST_REQUIRE(_libraryAddresses.empty()); std::vector errors; - bytes bytecode = eth::compileLLL(_sourceCode, m_optimize, &errors); + bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), m_optimize, &errors); if (!errors.empty()) { for (auto const& error: errors) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 105a0398702b..fd2017f922e1 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -56,7 +56,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework ASTNode const& sourceUnit = m_compiler.ast(""); BOOST_REQUIRE(items != nullptr); m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator::structuralEstimation(*items, vector({&sourceUnit})), + GasEstimator(dev::test::Options::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), {&sourceUnit} ); } @@ -65,7 +65,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework { compileAndRun(_sourceCode); auto state = make_shared(); - PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName())); + PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), dev::test::Options::get().evmVersion()); GasMeter::GasConsumption gas = meter.estimateMax(0, state); u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size()); // costs for deployment @@ -74,7 +74,7 @@ class GasMeterTestFramework: public SolidityExecutionFramework gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments) @@ -91,12 +91,12 @@ class GasMeterTestFramework: public SolidityExecutionFramework gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false)); } - gas += GasEstimator::functionalEstimation( + gas += GasEstimator(dev::test::Options::get().evmVersion()).functionalEstimation( *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK(gas.value == m_gasUsed); + BOOST_CHECK_EQUAL(gas.value, m_gasUsed); } static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) From 477571e19015f1c7806526d58d0ddffd91fe5823 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 19:22:59 +0100 Subject: [PATCH 110/242] Do not warn about 0.5.0 experimental pragma. --- Changelog.md | 1 + libsolidity/ast/ExperimentalFeatures.h | 1 + test/libsolidity/SolidityNameAndTypeResolution.cpp | 9 +++------ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c875a8fef66..04a45f7dd2b6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. + * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 3ecfac7bfce3..a17778b4f128 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -40,6 +40,7 @@ static const std::map ExperimentalFeatureOnlyAnalysis { { ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::TestOnlyAnalysis, true }, + { ExperimentalFeature::V050, true } }; static const std::map ExperimentalFeatureNames = diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4fb628215dc1..e503c22b11f7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -102,7 +102,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050) )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ "This declaration shadows an existing declaration.", - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -133,7 +132,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -164,7 +162,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Experimental features", "Unused local variable", "Unused local variable" })); @@ -262,7 +259,7 @@ BOOST_AUTO_TEST_CASE(scoping_for) } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(scoping_for2) @@ -276,7 +273,7 @@ BOOST_AUTO_TEST_CASE(scoping_for2) } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(scoping_for3) @@ -7674,7 +7671,7 @@ BOOST_AUTO_TEST_CASE(non_external_fallback) function () external { } } )"; - CHECK_WARNING(text, "Experimental features are turned on."); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( pragma experimental "v0.5.0"; contract C { From 298bdeec4921bd30c055a790976542989710b867 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 13:28:27 +0100 Subject: [PATCH 111/242] Remove unstable test case. --- test/libsolidity/SolidityEndToEndTest.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index e61afb0ea76e..c352a2c22518 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2481,25 +2481,6 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } -BOOST_AUTO_TEST_CASE(gas_left) -{ - char const* sourceCode = R"( - contract test { - function getGasLeft() public returns (uint256 val) { return msg.gas; } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); - - sourceCode = R"( - contract test { - function getGasLeft() public returns (uint256 val) { return gasleft(); } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getGasLeft()") == encodeArgs(99978604)); -} - BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( From 29fb5fe1c93ec6a5dd474e47f999236d1a8b6f0e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 13:28:57 +0100 Subject: [PATCH 112/242] Add test cases for shadowing gasleft with local functions/variables. --- .../SolidityNameAndTypeResolution.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 469211d31844..43c9bc9b960e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7434,6 +7434,25 @@ BOOST_AUTO_TEST_CASE(gas_left) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); } +BOOST_AUTO_TEST_CASE(gasleft_as_identifier) +{ + char const* text = R"( + contract C { + function gasleft() public pure returns (bytes32 val) { return "abc"; } + function f() public pure { bytes32 val = gasleft(); assert (val == "abc"); } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); + + text = R"( + contract C { + uint gasleft; + function f() public { gasleft = 42; } + } + )"; + CHECK_WARNING(text, "This declaration shadows a builtin symbol."); +} + BOOST_AUTO_TEST_CASE(builtin_reject_value) { char const* text = R"( From fd60c1cf86e69931fda47bdd187f444550ca785f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Sep 2017 14:54:41 +0100 Subject: [PATCH 113/242] Warn if using address overloads on contracts --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 10 + .../SolidityNameAndTypeResolution.cpp | 171 +++++++++++++++++- test/libsolidity/ViewPureChecker.cpp | 10 +- 4 files changed, 181 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c875a8fef66..67634ea041a4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. + * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. Bugfixes: diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 9846a0d09339..ac8b2b63f43d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1831,6 +1831,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (exprType->category() == Type::Category::Contract) { + // Warn about using address members on contracts + for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) + if (addressMember.name == memberName && *annotation.type == *addressMember.type) + m_errorReporter.warning( + _memberAccess.location(), + "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." + + " Convert the contract to \"address\" type to access the member." + ); + + // Warn about using send or transfer with a non-payable fallback function. if (auto callType = dynamic_cast(type(_memberAccess).get())) { auto kind = callType->kind(); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4fb628215dc1..d4962b6c58a8 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7011,6 +7011,8 @@ BOOST_AUTO_TEST_CASE(callable_crash) BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A's fallback function is not payable. char const* text = R"( contract A { function() public {} @@ -7024,12 +7026,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) { - char const* text = R"( + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A {} contract B { @@ -7040,12 +7047,17 @@ BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) { - char const* text = R"( + // This used to be a test for a.send to generate a warning + // because A does not have a payable fallback function. + std::string text = R"( contract A { function() public {} } @@ -7058,11 +7070,16 @@ BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) } } )"; - CHECK_ERROR(text, TypeError, "Value transfer to a contract without a payable fallback function."); + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) { + // This used to be a test for a.transfer to generate a warning + // because A does not have a payable fallback function. char const* text = R"( contract A { function() payable public {} @@ -7076,7 +7093,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "Using contract member \"transfer\" inherited from the address type is deprecated."); } BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) @@ -7992,6 +8009,134 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) CHECK_ERROR(text, TypeError, "Operator / not compatible with types int_const 3 and int_const 0"); } +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_contract) +{ + std::string text = R"( + contract C { + function f() view public { + this.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + this.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated."); + text = R"( + contract C { + function f() view public { + this.callcode; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"callcode\" inherited from the address type is deprecated"}, + {Error::Type::Warning, "\"callcode\" has been deprecated in favour of \"delegatecall\""} + })); + text = R"( + contract C { + function f() view public { + this.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated."); +} + +BOOST_AUTO_TEST_CASE(warn_about_address_members_on_non_this_contract) +{ + std::string text = R"( + contract C { + function f() view public { + C c; + c.balance; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() view public { + C c; + c.transfer; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() view public { + C c; + c.send; + } + } + )"; + CHECK_ALLOW_MULTI(text, (vector>{ + {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, + {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} + })); + text = R"( + contract C { + function f() pure public { + C c; + c.call; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated"); + text = R"( + contract C { + function f() pure public { + C c; + c.callcode; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (std::vector{ + "Using contract member \"callcode\" inherited from the address type is deprecated", + "\"callcode\" has been deprecated in favour of \"delegatecall\"" + })); + text = R"( + contract C { + function f() pure public { + C c; + c.delegatecall; + } + } + )"; + CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated"); +} + BOOST_AUTO_TEST_CASE(no_address_members_on_contract) { char const* text = R"( @@ -8050,6 +8195,20 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function transfer(uint) public; + function f() public { + this.transfer(10); + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + BOOST_AUTO_TEST_CASE(emit_events) { char const* text = R"( diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index e6a5cfd06bdc..6e8620f9e743 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -279,11 +279,11 @@ BOOST_AUTO_TEST_CASE(builtin_functions) string text = R"( contract C { function f() public { - this.transfer(1); - require(this.send(2)); - selfdestruct(this); - require(this.delegatecall()); - require(this.call()); + address(this).transfer(1); + require(address(this).send(2)); + selfdestruct(address(this)); + require(address(this).delegatecall()); + require(address(this).call()); } function g() pure public { bytes32 x = keccak256("abc"); From 1ceb0b04c1ce117366cde88eb8c3bebb1a5e9598 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 15:55:02 +0100 Subject: [PATCH 114/242] Assert that address members are not present on contract types in 0.5.0. --- libsolidity/analysis/TypeChecker.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ac8b2b63f43d..106a2c8e860f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1832,13 +1832,17 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (exprType->category() == Type::Category::Contract) { // Warn about using address members on contracts + bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) if (addressMember.name == memberName && *annotation.type == *addressMember.type) + { + solAssert(!v050, "Address member still present on contract in v0.5.0."); m_errorReporter.warning( _memberAccess.location(), "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." + " Convert the contract to \"address\" type to access the member." ); + } // Warn about using send or transfer with a non-payable fallback function. if (auto callType = dynamic_cast(type(_memberAccess).get())) From b8589fbe0f4eea7b74ce45c2dbaf1bbf710e7287 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 16:59:33 +0100 Subject: [PATCH 115/242] Use msg.value and gasleft() as an example in the docs. --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 5754b734dfe8..58e1dcdc677a 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be assigned from an expression which is a constant at compile time. Any expression that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or ``block.number``) or -execution data (``msg.value``) or make calls to external contracts are disallowed. Expressions +execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions that might have a side-effect on memory allocation are allowed, but those that might have a side-effect on other memory objects are not. The built-in functions ``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` From 3340053fd95b73facc8a1534a130f58698808f2e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 17:00:37 +0100 Subject: [PATCH 116/242] Style improvements. --- libsolidity/ast/Types.cpp | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bde0dd6ef7b9..26bde1c441b9 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,7 +3000,7 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const { solAssert(_contract, ""); const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); @@ -3025,7 +3025,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const *_contra }; if (!v050) members.emplace_back("gas", make_shared(256)); - return MemberList::MemberMap(members); + return members; } case Kind::Transaction: return MemberList::MemberMap({ diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d35993675fba..58e2aa627aec 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -924,7 +924,7 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { - const bool v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); + bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. @@ -1140,8 +1140,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (!v050 && member == "gas") + else if (member == "gas") + { + solAssert(!v050, ""); m_context << Instruction::GAS; + } else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") From 2213f9946b34a6e8f5604a6b821d31788dcee08b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 18:01:42 +0100 Subject: [PATCH 117/242] Improved gasleft tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 12 ++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c352a2c22518..ebb2f3ffd2f9 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5352,6 +5352,18 @@ BOOST_AUTO_TEST_CASE(super_overload) ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) +{ + char const* sourceCode = R"( + contract C { + function gasleft() returns(uint256) { return 0; } + function f() returns(uint256) { return gasleft(); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(bool_conversion) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 43c9bc9b960e..f08f695b51b5 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7409,21 +7409,21 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); } -BOOST_AUTO_TEST_CASE(gas_left) +BOOST_AUTO_TEST_CASE(gasleft) { char const* text = R"( contract C { - function f() public returns (uint256 val) { return msg.gas; } + function f() public view returns (uint256 val) { return msg.gas; } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( contract C { - function f() public returns (uint256 val) { return gasleft(); } + function f() public view returns (uint256 val) { return gasleft(); } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); text = R"( pragma experimental "v0.5.0"; @@ -7434,12 +7434,12 @@ BOOST_AUTO_TEST_CASE(gas_left) CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); } -BOOST_AUTO_TEST_CASE(gasleft_as_identifier) +BOOST_AUTO_TEST_CASE(gasleft_shadowing) { char const* text = R"( contract C { function gasleft() public pure returns (bytes32 val) { return "abc"; } - function f() public pure { bytes32 val = gasleft(); assert (val == "abc"); } + function f() public pure returns (bytes32 val) { return gasleft(); } } )"; CHECK_WARNING(text, "This declaration shadows a builtin symbol."); From 83fcf007bfdf9c9c8b595369c0542d45943ee095 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 26 Feb 2018 18:25:36 +0100 Subject: [PATCH 118/242] Do not retain any gas in external calls (except if EVM version is set to homestead). --- Changelog.md | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 23 ++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 04a45f7dd2b6..529fb1814c8a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 12881d637d06..441e4429af1c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1671,16 +1671,19 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().storeFreeMemoryPointer(); } - // Touch the end of the output area so that we do not pay for memory resize during the call - // (which we would have to subtract from the gas left) - // We could also just use MLOAD; POP right before the gas calculation, but the optimizer - // would remove that, so we use MSTORE here. - if (!_functionType.gasSet() && retSize > 0) + if (!m_context.evmVersion().canOverchargeGasForCall()) { - m_context << u256(0); - utils().fetchFreeMemoryPointer(); - // This touches too much, but that way we save some rounding arithmetics - m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; + // Touch the end of the output area so that we do not pay for memory resize during the call + // (which we would have to subtract from the gas left) + // We could also just use MLOAD; POP right before the gas calculation, but the optimizer + // would remove that, so we use MSTORE here. + if (!_functionType.gasSet() && retSize > 0) + { + m_context << u256(0); + utils().fetchFreeMemoryPointer(); + // This touches too much, but that way we save some rounding arithmetics + m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; + } } // Copy function identifier to memory. @@ -1749,7 +1752,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.gasSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); - else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050)) + else if (m_context.evmVersion().canOverchargeGasForCall()) // Send all gas (requires tangerine whistle EVM) m_context << Instruction::GAS; else From 174c46d5afd8be4e6b23804bbdd8245d5a78cc0e Mon Sep 17 00:00:00 2001 From: Anthony Broad-Crawford Date: Fri, 2 Mar 2018 15:30:03 -0600 Subject: [PATCH 119/242] Improved messaging when an error spans multiple lines and updated change log file. --- Changelog.md | 1 + libsolidity/interface/SourceReferenceFormatter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index a28538ed79eb..ce0cc3a2586a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Features: * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. + * Improved messaging when error spans multiple lines of a sourcefile Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 9d02c4980dca..0f0143721489 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -79,8 +79,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati scanner.lineAtPosition(_location->start) << endl << string(startColumn, ' ') << - "^\n" << - "Spanning multiple lines.\n"; + "^ (Relevant source part starts here and spans across multiple lines)." << + endl; } void SourceReferenceFormatter::printSourceName(SourceLocation const* _location) From 91b13b734d34e6bb5a76fb53624588d035946237 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 23:13:51 +0100 Subject: [PATCH 120/242] Use StaticAnalyzer to deprecate msg.gas instead of conditionally removing it in MagicType. --- libsolidity/analysis/StaticAnalyzer.cpp | 19 ++++++++++++++++++- libsolidity/ast/Types.cpp | 14 ++++---------- libsolidity/codegen/ExpressionCompiler.cpp | 5 ----- .../SolidityNameAndTypeResolution.cpp | 4 ++-- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index bd8ee59762d0..d4de219af900 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -139,6 +139,23 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement) bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) { + bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) + if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") + { + if (v050) + m_errorReporter.typeError( + _memberAccess.location(), + "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" + ); + } + if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value") @@ -151,7 +168,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (auto const* type = dynamic_cast(_memberAccess.annotation().type.get())) if (type->kind() == FunctionType::Kind::BareCallCode) { - if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + if (v050) m_errorReporter.typeError( _memberAccess.location(), "\"callcode\" has been deprecated in favour of \"delegatecall\"." diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 26bde1c441b9..771ae64393e0 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3000,10 +3000,8 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { - solAssert(_contract, ""); - const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: @@ -3016,17 +3014,13 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contra {"gaslimit", make_shared(256)} }); case Kind::Message: - { - std::vector members = { + return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, + {"gas", make_shared(256)}, {"value", make_shared(256)}, {"data", make_shared(DataLocation::CallData)}, {"sig", make_shared(4)} - }; - if (!v050) - members.emplace_back("gas", make_shared(256)); - return members; - } + }); case Kind::Transaction: return MemberList::MemberMap({ {"origin", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58e2aa627aec..4c6e9a56a463 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -924,8 +924,6 @@ bool ExpressionCompiler::visit(NewExpression const&) bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { - bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); - CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); // Check whether the member is a bound function. ASTString const& member = _memberAccess.memberName(); @@ -1141,10 +1139,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "origin") m_context << Instruction::ORIGIN; else if (member == "gas") - { - solAssert(!v050, ""); m_context << Instruction::GAS; - } else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f08f695b51b5..3b0ccd4def48 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7416,7 +7416,7 @@ BOOST_AUTO_TEST_CASE(gasleft) function f() public view returns (uint256 val) { return msg.gas; } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); text = R"( contract C { @@ -7431,7 +7431,7 @@ BOOST_AUTO_TEST_CASE(gasleft) function f() public returns (uint256 val) { return msg.gas; } } )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup in msg"); + CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); } BOOST_AUTO_TEST_CASE(gasleft_shadowing) From d8bfa02deb11007126094a3b05bbf397ba23eed2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 19:50:09 +0100 Subject: [PATCH 121/242] Try new caching keys. --- circle.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 1ed09ada0281..3f9724f44c8a 100644 --- a/circle.yml +++ b/circle.yml @@ -96,14 +96,16 @@ jobs: if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - restore_cache: - key: ccache-{{ arch }}-{{ .Branch }} - key: ccache-{{ arch }} - key: ccache + key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + key: ccache-{{ arch }}-{{ .Branch }}- + key: ccache-{{ arch }}-develop- + key: ccache-{{ arch }}- - run: name: Build command: ./scripts/build.sh RelWithDebInfo - save_cache: - key: ccache-{{ arch }}-{{ .Branch }} + key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} + when: always paths: - ~/.ccache - store_artifacts: From 8259ce1e94ee6a4d33ca16f3f4d3616398e2de8c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 22:49:27 +0100 Subject: [PATCH 122/242] Restrict ccache cache size. --- circle.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/circle.yml b/circle.yml index 3f9724f44c8a..990d39390de3 100644 --- a/circle.yml +++ b/circle.yml @@ -100,9 +100,15 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }}- key: ccache-{{ arch }}-develop- key: ccache-{{ arch }}- + - run: + name: Configure ccache + command: ccache -M 80M && ccache -c && ccache -s && ccache -z - run: name: Build command: ./scripts/build.sh RelWithDebInfo + - run: + name: CCache statistics + command: ccache -s - save_cache: key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} when: always From 556fe8a574d6494f68a28267e30808c0448ddcb1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 5 Mar 2018 22:56:35 +0100 Subject: [PATCH 123/242] Build with four parallel jobs. --- circle.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 990d39390de3..45a26fa8f61d 100644 --- a/circle.yml +++ b/circle.yml @@ -105,7 +105,11 @@ jobs: command: ccache -M 80M && ccache -c && ccache -s && ccache -z - run: name: Build - command: ./scripts/build.sh RelWithDebInfo + command: | + mkdir -p build + cd build + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo + make -j4 - run: name: CCache statistics command: ccache -s From 154fd1d6cbfb7fc954b78e82831c8cdf50a51657 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 00:59:34 +0100 Subject: [PATCH 124/242] Document the meaning of sourceIndex "-1" in source mappings. --- docs/miscellaneous.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7d3d058b94a5..075b6be02beb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -192,6 +192,11 @@ These are regular array indices into a list of source files usually called ``"sourceList"``, which is part of the combined-json and the output of the json / npm compiler. +.. note :: + In the case of instructions that are not associated with any particular source file, + the source mapping assigns an integer identifier of ``-1``. This may happen for + bytecode sections stemming from compiler-generated inline assembly statements. + The source mappings inside the AST use the following notation: From 5d486b741fc5352bb7d02a812fb7566f1749f9a7 Mon Sep 17 00:00:00 2001 From: Oleksii Matiiasevych Date: Tue, 6 Mar 2018 14:19:59 +0700 Subject: [PATCH 125/242] Revert warning update about `view` modifier --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index e61667dddf7f..67d4a24918ba 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -473,7 +473,7 @@ The following statements are considered modifying the state: Getter methods are marked ``view``. .. warning:: - Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state. + The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though. .. index:: ! pure function, function;pure From 6055bc250fb393cf621f6c7e63a2d89b880fe3a6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 10:32:58 +0100 Subject: [PATCH 126/242] Use coding style. --- CODING_STYLE.md | 254 ++++++++++++++++++++++++++++++++++++++++++ docs/contributing.rst | 4 +- 2 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 CODING_STYLE.md diff --git a/CODING_STYLE.md b/CODING_STYLE.md new file mode 100644 index 000000000000..ee924e726dcd --- /dev/null +++ b/CODING_STYLE.md @@ -0,0 +1,254 @@ +0. Formatting + +GOLDEN RULE: Follow the style of the existing code when you make changes. + +a. Use tabs for leading indentation +- tab stops are every 4 characters. +- One indentation level -> exactly one byte (i.e. a tab character) in the source file. +- If you have run-on lines, indent as you would for a block. +b. Line widths: +- Don't worry about having lines of code > 80-char wide. +- Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. +c. Don't use braces for condition-body one-liners. +d. Never place condition bodies on same line as condition. +e. Space between first paren and keyword, but *not* following first paren or preceding final paren. +f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. +g. No spaces for subscripting or unary operators. +h. No space before ':' but one after it, except in the ternary operator: one on both sides. +i. Space all other operators. +j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. + +(WRONG) +if( a==b[ i ] ) { printf ("Hello\n"); } +foo->bar(someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName); + +(RIGHT) +if (a == b[i]) + printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'. +foo->bar( + someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName +); + + + +1. Namespaces; + +a. No "using namespace" declarations in header files. +b. All symbols should be declared in a namespace except for final applications. +c. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. + +(WRONG) +#include +using namespace std; +tuple meanAndSigma(vector const& _v); + +(CORRECT) +#include +std::tuple meanAndSigma(std::vector const& _v); + + + +2. Preprocessor; + +a. File comment is always at top, and includes: +- Copyright. +- License (e.g. see COPYING). +b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment. +c. Prefer static const variable to value macros. +d. Prefer inline constexpr functions to function macros. +e. Split complex macro on multiple lines with '\'. + + + +3. Capitalization; + +GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase. + +a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. +b. The following entities' first alpha is upper case: +- Type names. +- Template parameters. +- Enum members. +- static const variables that form an external API. +c. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation. + + +All other entities' first alpha is lower case. + + + +4. Variable prefixes: + +a. Leading underscore "_" to parameter names. +- Exception: "o_parameterName" when it is used exclusively for output. See 6(f). +- Exception: "io_parameterName" when it is used for both input and output. See 6(f). +b. Leading "c_" to const variables (unless part of an external API). +c. Leading "g_" to global (non-const) variables. +d. Leading "s_" to static (non-const, non-global) variables. + + + +5. Error reporting: + +- Prefer exception to bool/int return type. + + + +6. Declarations: + +a. {Typename} + {qualifiers} + {name}. +b. Only one per line. +c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). +d. Favour declarations close to use; don't habitually declare at top of scope ala C. +e. Always pass non-trivial parameters with a const& suffix. +f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. +g. Never use a macro where adequate non-preprocessor C++ can be written. +h. Make use of auto whenever type is clear or unimportant: +- Always avoid doubly-stating the type. +- Use to avoid vast and unimportant type declarations. +- However, avoid using auto where type is not immediately obvious from the context, and especially not for arithmetic expressions. +i. Don't pass bools: prefer enumerations instead. +j. Prefer enum class to straight enum. + + +(WRONG) +const double d = 0; +int i, j; +char *s; +float meanAndSigma(std::vector _v, float* _sigma, bool _approximate); +Derived* x(dynamic_cast(base)); +for (map::iterator i = l.begin(); i != l.end(); ++l) {} + + +(CORRECT) +enum class Accuracy +{ + Approximate, + Exact +}; +double const d = 0; +int i; +int j; +char* s; +std::tuple meanAndSigma(std::vector const& _v, Accuracy _a); +auto x = dynamic_cast(base); +for (auto i = x.begin(); i != x.end(); ++i) {} + + +7. Structs & classes + +a. Structs to be used when all members public and no virtual functions. +- In this case, members should be named naturally and not prefixed with 'm_' +b. Classes to be used in all other circumstances. + + + +8. Members: + +a. One member per line only. +b. Private, non-static, non-const fields prefixed with m_. +c. Avoid public fields, except in structs. +d. Use override, final and const as much as possible. +e. No implementations with the class declaration, except: +- template or force-inline method (though prefer implementation at bottom of header file). +- one-line implementation (in which case include it in same line as declaration). +f. For a property 'foo' +- Member: m_foo; +- Getter: foo() [ also: for booleans, isFoo() ]; +- Setter: setFoo(); + + + +9. Naming + +a. Collection conventions: +- -s means std::vector e.g. using MyTypes = std::vector +- -Set means std::set e.g. using MyTypeSet = std::set +- -Hash means std::unordered_set e.g. using MyTypeHash = std::unordered_set +b. Class conventions: +- -Face means the interface of some shared concept. (e.g. FooFace might be a pure virtual class.) +c. Avoid unpronouncable names; +- If you need to shorten a name favour a pronouncable slice of the original to a scattered set of consonants. +- e.g. Manager shortens to Man rather than Mgr. +d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) +e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +- A dictionary and thesaurus are your friends. +- Spell correctly. +- Think carefully about the class's purpose. +- Imagine it as an isolated component to try to decontextualise it when considering its name. +- Don't be trapped into naming it (purely) in terms of its implementation. + + + +10. Type-definitions + +a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector; rather than typedef std::vector ints; +b. Generally avoid shortening a standard form that already includes all important information: +- e.g. stick to shared_ptr rather than shortening to ptr. +c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. +- e.g. using Guard = std::lock_guard; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly. +d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. + + + +11. Commenting + +a. Comments should be doxygen-compilable, using @notation rather than \notation. +b. Document the interface, not the implementation. +- Documentation should be able to remain completely unchanged, even if the method is reimplemented. +- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). +- Be careful to scrutinise documentation that extends only to intended purpose and usage. +- Reject documentation that is simply an English transaction of the implementation. + + + +12. Include Headers + +Includes should go in increasing order of generality (libethereum -> libethcore -> libdevcrypto -> libdevcore -> boost -> STL). For example: + +#include +#include +#include +#include +#include +#include +#include +#include + +See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. + + + +13. Logging + +Logging should be performed at appropriate verbosities depending on the logging message. +The more likely a message is to repeat (and thus cause noise) the higher in verbosity it should be. +Some rules to keep in mind: + + - Verbosity == 0 -> Reserved for important stuff that users must see and can understand. + - Verbosity == 1 -> Reserved for stuff that users don't need to see but can understand. + - Verbosity >= 2 -> Anything that is or might be displayed more than once every minute + - Verbosity >= 3 -> Anything that only a developer would understand + - Verbosity >= 4 -> Anything that is low-level (e.g. peer disconnects, timers being cancelled) + + +14. Recommended reading + +Herb Sutter and Bjarne Stroustrup +- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) + +Herb Sutter and Andrei Alexandrescu +- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices" + +Scott Meyers +- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)" +- "More Effective C++: 35 New Ways to Improve Your Programs and Designs" +- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" diff --git a/docs/contributing.rst b/docs/contributing.rst index a5efba8bf789..8c190a266f36 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -55,8 +55,8 @@ However, if you are making a larger change, please consult with the `Solidity De focused on compiler and language development instead of language use) first. -Finally, please make sure you respect the `coding standards -`_ +Finally, please make sure you respect the `coding style +`_ for this project. Also, even though we do CI testing, please test your code and ensure that it builds locally before submitting a pull request. From d7fe0bbc7227d7f57043a686bac7bd9bb9e49642 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 11:11:33 +0100 Subject: [PATCH 127/242] Increase ccache size. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 45a26fa8f61d..263cb700ea45 100644 --- a/circle.yml +++ b/circle.yml @@ -102,7 +102,7 @@ jobs: key: ccache-{{ arch }}- - run: name: Configure ccache - command: ccache -M 80M && ccache -c && ccache -s && ccache -z + command: ccache -M 200M && ccache -c && ccache -s && ccache -z - run: name: Build command: | From e3bd3020d13bd898be8c6123b9979c69422e944f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 11:04:29 +0100 Subject: [PATCH 128/242] Use new eth binary for trusty. --- scripts/tests.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 60dae2e428f5..bf4ee3d9d506 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -59,12 +59,15 @@ function download_eth() ETH_PATH="eth" else mkdir -p /tmp/test - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" if grep -i trusty /etc/lsb-release >/dev/null 2>&1 then - ETH_BINARY=eth_byzantium2 - ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06 + ETH_BINARY=eth_byzantium_trusty + ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b" + else + # built from ?? at 2018-02-13 ? + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" fi wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" @@ -121,4 +124,4 @@ wait $CMDLINE_PID pkill "$ETH_PID" || true sleep 4 -pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file +pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true From b467116ea85383d5c8492e7f21ea16ce7c70ebc9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 26 Sep 2017 15:27:03 +0200 Subject: [PATCH 129/242] Use STATICCALL for pure function calls if EVM version supports it and 0.5.0 is activated. --- Changelog.md | 3 ++- libsolidity/ast/ExperimentalFeatures.h | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 9 +++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index bea9dd5b691c..4a9f7ff4c211 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,8 +2,9 @@ Features: * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. - * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). + * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index a17778b4f128..30ea7ec566d3 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -29,8 +29,8 @@ namespace solidity enum class ExperimentalFeature { - SMTChecker, ABIEncoderV2, // new ABI encoder that makes use of JULIA + SMTChecker, V050, // v0.5.0 breaking changes Test, TestOnlyAnalysis @@ -45,8 +45,8 @@ static const std::map ExperimentalFeatureOnlyAnalysis static const std::map ExperimentalFeatureNames = { - { "SMTChecker", ExperimentalFeature::SMTChecker }, { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, + { "SMTChecker", ExperimentalFeature::SMTChecker }, { "v0.5.0", ExperimentalFeature::V050 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d27af7db85b8..7162cb0dd28f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1610,6 +1610,10 @@ void ExpressionCompiler::appendExternalFunctionCall( bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall; bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; + bool useStaticCall = + _functionType.stateMutability() <= StateMutability::View && + m_context.experimentalFeatureActive(ExperimentalFeature::V050) && + m_context.evmVersion().hasStaticCall(); unsigned retSize = 0; if (returnSuccessCondition) @@ -1738,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall( // [value,] addr, gas (stack top) if (isDelegateCall) solAssert(!_functionType.valueSet(), "Value set for delegatecall"); + else if (useStaticCall) + solAssert(!_functionType.valueSet(), "Value set for staticcall"); else if (_functionType.valueSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else @@ -1769,10 +1775,13 @@ void ExpressionCompiler::appendExternalFunctionCall( gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB; } + // Order is important here, STATICCALL might overlap with DELEGATECALL. if (isDelegateCall) m_context << Instruction::DELEGATECALL; else if (isCallCode) m_context << Instruction::CALLCODE; + else if (useStaticCall) + m_context << Instruction::STATICCALL; else m_context << Instruction::CALL; From a3593df43b8d5639dd756d238cc744b1204cc3e8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Jan 2018 16:50:00 +0100 Subject: [PATCH 130/242] Test for using staticcall for view and pure functions. --- test/libsolidity/SolidityEndToEndTest.cpp | 60 +++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ebb2f3ffd2f9..33cd1419c9ef 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,13 +21,20 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ +#include + +#include + +#include +#include + +#include + +#include + #include #include #include -#include -#include -#include -#include using namespace std; using namespace std::placeholders; @@ -10778,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark) BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + uint x; + function f() public returns (uint) { + x = 3; + return 1; + } + } + interface CView { + function f() view external returns (uint); + } + interface CPure { + function f() pure external returns (uint); + } + contract D { + function f() public returns (uint) { + return (new C()).f(); + } + function fview() public returns (uint) { + return (CView(new C())).f(); + } + function fpure() public returns (uint) { + return (CPure(new C())).f(); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + // This should work (called via CALL) + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + // These should throw (called via STATICCALL) + ABI_CHECK(callContractFunction("fview()"), encodeArgs()); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); + } + else + { + ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); + } +} + BOOST_AUTO_TEST_SUITE_END() } From 3057aeece495276265d9632b97e3faffcb57fe71 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 16 Jan 2018 10:31:13 +0100 Subject: [PATCH 131/242] Document STATICCALL usage in experimental 0.5.0. --- docs/contracts.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 67d4a24918ba..121c4de0edd9 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -472,6 +472,13 @@ The following statements are considered modifying the state: .. note:: Getter methods are marked ``view``. +.. note:: + If invalid explicit type conversions are used, state modifications are possible + even though a ``view`` function was called. + You can switch the compiler to use ``STATICCALL`` when calling such functions and thus + prevent modifications to the state on the level of the EVM by adding + ``pragma experimental "v0.5.0";`` + .. warning:: The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though. @@ -502,6 +509,18 @@ In addition to the list of state modifying statements explained above, the follo } } +.. note:: + If invalid explicit type conversions are used, state modifications are possible + even though a ``pure`` function was called. + You can switch the compiler to use ``STATICCALL`` when calling such functions and thus + prevent modifications to the state on the level of the EVM by adding + ``pragma experimental "v0.5.0";`` + +.. warning:: + It is not possible to prevent functions from reading the state at the level + of the EVM, it is only possible to prevent them from writing to the state + (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). + .. warning:: Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state. From fb4d01e2cd9c3d6fa862a5c9fea433522b94d916 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 17:18:57 +0100 Subject: [PATCH 132/242] Add gasleft to FunctionType::richIdentifier(). --- libsolidity/ast/Types.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6a9707f3e824..c08e0e673479 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2344,6 +2344,7 @@ string FunctionType::richIdentifier() const case Kind::Log2: id += "log2"; break; case Kind::Log3: id += "log3"; break; case Kind::Log4: id += "log4"; break; + case Kind::GasLeft: id += "gasleft"; break; case Kind::Event: id += "event"; break; case Kind::SetGas: id += "setgas"; break; case Kind::SetValue: id += "setvalue"; break; From fd1662d1c49776232b491f1871391ad1cd90309a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 15:18:09 +0100 Subject: [PATCH 133/242] Warn about using loose inline assembly features as experimental 0.5.0 feature. --- libsolidity/analysis/ReferencesResolver.cpp | 3 +- libsolidity/analysis/TypeChecker.cpp | 5 +++ libsolidity/codegen/CompilerContext.cpp | 1 + libsolidity/inlineasm/AsmAnalysis.cpp | 45 +++++++++++++++++---- libsolidity/inlineasm/AsmAnalysis.h | 17 +++++++- libsolidity/interface/AssemblyStack.cpp | 2 +- test/libjulia/Common.cpp | 1 + test/libjulia/Parser.cpp | 1 + 8 files changed, 65 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 296a39c23bb3..f91eaf6eb27a 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -280,7 +280,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information // We use the latest EVM version because we will re-run it anyway. assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + boost::optional errorTypeForLoose = m_experimental050Mode ? Error::Type::SyntaxError : Error::Type::Warning; + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1748b518cf02..5d28e776ee41 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -894,10 +894,15 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); _inlineAssembly.annotation().analysisInfo = make_shared(); + boost::optional errorTypeForLoose = + m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) ? + Error::Type::SyntaxError : + Error::Type::Warning; assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, m_evmVersion, + errorTypeForLoose, assembly::AsmFlavour::Loose, identifierAccess ); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ebf0213ad6a6..0bf88267f05d 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -330,6 +330,7 @@ void CompilerContext::appendInlineAssembly( analysisInfo, errorReporter, m_evmVersion, + boost::none, assembly::AsmFlavour::Strict, identifierAccess.resolve ).analyze(*parserResult); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index a7f764a5b925..abf7ddf26fa2 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -54,7 +54,10 @@ bool AsmAnalyzer::analyze(Block const& _block) bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _label.location, + "The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead." + ); m_info.stackHeightInfo[&_label] = m_stackHeight; warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; @@ -62,7 +65,10 @@ bool AsmAnalyzer::operator()(Label const& _label) bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _instruction.location, + "The use of non-functional instructions is deprecated. Please use functional notation instead." + ); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instruction] = m_stackHeight; @@ -170,18 +176,31 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) { - size_t initialStackHeight = m_stackHeight; + int initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); - if (m_flavour != AsmFlavour::Loose) - if (!expectDeposit(0, initialStackHeight, _statement.location)) + if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose)) + { + Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; + string msg = + "Top-level expressions are not supposed to return values (this expression returns " + + boost::lexical_cast(m_stackHeight - initialStackHeight) + + " value" + + (m_stackHeight - initialStackHeight == 1 ? "" : "s") + + "). Use ``pop()`` or assign them."; + m_errorReporter.error(errorType, _statement.location, msg); + if (errorType != Error::Type::Warning) success = false; + } m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + checkLooseFeature( + _assignment.location, + "The use of stack assignment is deprecated. Please use assignment in functional notation instead." + ); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; @@ -577,10 +596,22 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio ); if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) - m_errorReporter.warning( + { + solAssert(m_flavour == AsmFlavour::Loose, ""); + m_errorReporter.error( + m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, _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\", \"if\" or \"for\" statements instead." ); + } +} + +void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) +{ + if (m_flavour != AsmFlavour::Loose) + solAssert(false, _description); + else if (m_errorTypeForLoose) + m_errorReporter.error(*m_errorTypeForLoose, _location, _description); } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 867711c71a9e..8d2a71f063b8 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -56,9 +57,17 @@ class AsmAnalyzer: public boost::static_visitor AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, EVMVersion _evmVersion, + boost::optional _errorTypeForLoose, AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {} + ): + m_resolver(_resolver), + m_info(_analysisInfo), + m_errorReporter(_errorReporter), + m_evmVersion(_evmVersion), + m_flavour(_flavour), + m_errorTypeForLoose(_errorTypeForLoose) + {} bool analyze(assembly::Block const& _block); @@ -91,6 +100,11 @@ class AsmAnalyzer: public boost::static_visitor void expectValidType(std::string const& type, SourceLocation const& _location); void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location); + /// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler + /// exception (if the flavour is not Loose), reports an error/warning + /// (if m_errorTypeForLoose is set) or does nothing. + void checkLooseFeature(SourceLocation const& _location, std::string const& _description); + int m_stackHeight = 0; julia::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; @@ -101,6 +115,7 @@ class AsmAnalyzer: public boost::static_visitor ErrorReporter& m_errorReporter; EVMVersion m_evmVersion; AsmFlavour m_flavour = AsmFlavour::Loose; + boost::optional m_errorTypeForLoose; }; } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 7a9fffbf299a..7f97336b0541 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann bool AssemblyStack::analyzeParsed() { m_analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language)); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); m_analysisSuccessful = analyzer.analyze(*m_parserResult); return m_analysisSuccessful; } diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index d8cd20b6147c..41f5d320619f 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -67,6 +67,7 @@ pair, shared_ptr> dev::julia::test: *analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), + boost::none, flavour ); if (analyzer.analyze(*parserResult)) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 6476c4d4589e..df905dd68fa8 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -60,6 +60,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter) analysisInfo, errorReporter, dev::test::Options::get().evmVersion(), + boost::none, assembly::AsmFlavour::IULIA )).analyze(*parserResult); } From 84d711fd1d38d36eb6ef2d3c118694a9ad06938e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 17:25:54 +0100 Subject: [PATCH 134/242] Tests for warnings/errors for loose assembly. --- .../SolidityNameAndTypeResolution.cpp | 196 +++++++++++++++++- 1 file changed, 192 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 87d629735963..71d3e93eb300 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5946,10 +5946,11 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) { char const* text = R"( + pragma experimental "v0.5.0"; contract c { uint8 x; function f() public { - assembly { x pop } + assembly { pop(x) } } } )"; @@ -5959,6 +5960,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { modifier m { uint a = 1; @@ -5967,7 +5969,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) } _; } - function f() m { + function f() public m { } } )"; @@ -5977,6 +5979,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) BOOST_AUTO_TEST_CASE(inline_assembly_storage) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; function f() public { @@ -5992,6 +5995,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage) BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint x = 1; modifier m { @@ -6000,7 +6004,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) } _; } - function f() m { + function f() public m { } } )"; @@ -6010,6 +6014,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -6025,6 +6030,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint constant x = 1; function f() public { @@ -6040,6 +6046,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { function f() public { uint a; @@ -6055,6 +6062,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint[] r; function f() public { @@ -6071,6 +6079,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_stor BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) { char const* text = R"( + pragma experimental "v0.5.0"; contract test { uint a; function f() pure public { @@ -6101,6 +6110,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) { char const* text = R"( + pragma experimental "v0.5.0"; contract C { function f(bytes bytesAsCalldata) external { assembly { @@ -6112,6 +6122,182 @@ BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) CHECK_ERROR(text, TypeError, "Call data elements cannot be accessed directly."); } +BOOST_AUTO_TEST_CASE(inline_assembly_050_literals_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_literals_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + 1 + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_bare_instructions) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "The use of non-functional"}, + {Error::Type::SyntaxError, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_bare_instructions) +{ + char const* text = R"( + contract C { + function f() view public { + assembly { + address + pop + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "The use of non-functional"}, + {Error::Type::Warning, "The use of non-functional"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_labels) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"}, + {Error::Type::SyntaxError, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_labels) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + label: + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Jump instructions and labels are low-level"}, + {Error::Type::Warning, "The use of labels is deprecated"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_jump) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_jump) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + jump(2) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::TypeError, "Function declared as pure"}, + {Error::Type::Warning, "Jump instructions and labels are low-level"} + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_050_leave_items_on_stack) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::SyntaxError, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_leave_items_on_stack) +{ + char const* text = R"( + contract C { + function f() pure public { + assembly { + mload(0) + } + } + } + )"; + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "are not supposed to return"}, + {Error::Type::DeclarationError, "Unbalanced stack"}, + })); +} + BOOST_AUTO_TEST_CASE(invalid_mobile_type) { char const* text = R"( @@ -7157,6 +7343,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) )"; vector> expectations(vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, + {Error::Type::Warning, "The use of non-functional instructions is deprecated."}, {Error::Type::DeclarationError, "Unbalanced stack"} }); if (!dev::test::Options::get().evmVersion().supportsReturndata()) @@ -7172,7 +7359,8 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) CHECK_ALLOW_MULTI(text, (std::vector>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, - {Error::Type::DeclarationError, "Unbalanced stack"} + {Error::Type::DeclarationError, "Unbalanced stack"}, + {Error::Type::Warning, "not supposed to return values"} })); } From af7c64c1fc56221736320582dd9644470f157fa3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Feb 2018 15:47:50 +0100 Subject: [PATCH 135/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c643c943de2b..b4ba572f78f0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. + * Inline Assembly: Enforce strict mode as experimental 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. From b4d38c5491fbde31dd36471ff442334cf906e180 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 18:09:54 +0100 Subject: [PATCH 136/242] Use double quotes for suggestion about hex literals and denominations. --- libsolidity/analysis/TypeChecker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1748b518cf02..6f55941fe4b7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2065,13 +2065,13 @@ void TypeChecker::endVisit(Literal const& _literal) m_errorReporter.fatalTypeError( _literal.location(), "Hexadecimal numbers cannot be used with unit denominations. " - "You can use an expression of the form '0x1234 * 1 day' instead." + "You can use an expression of the form \"0x1234 * 1 day\" instead." ); else m_errorReporter.warning( _literal.location(), "Hexadecimal numbers with unit denominations are deprecated. " - "You can use an expression of the form '0x1234 * 1 day' instead." + "You can use an expression of the form \"0x1234 * 1 day\" instead." ); } if (!_literal.annotation().type) From 7566787cd247f9a84c1adb5ced9356b9ada0fa8e Mon Sep 17 00:00:00 2001 From: ankit raj Date: Wed, 7 Mar 2018 00:09:53 +0530 Subject: [PATCH 137/242] Some words on Remix --- docs/index.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 184d0e6920ce..f53b0fc41293 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,7 +19,9 @@ crowdfunding, blind auctions, multi-signature wallets and more. .. note:: The best way to try out Solidity right now is using `Remix `_ - (it can take a while to load, please be patient). + (it can take a while to load, please be patient). Remix is a web browser + based IDE that allows you to write Solidity smart contracts, then deploy + and run the smart contracts. .. warning:: Since software is written by humans, it can have bugs. Thus, also From ea48d8ade34910838fb9c07ad5494d17935d250b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 20:15:43 +0100 Subject: [PATCH 138/242] Fix test. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 85ca39683a4d..997b610ea265 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8491,7 +8491,7 @@ BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members } } )"; - CHECK_WARNING(text, "Experimental features"); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(emit_events) From cd2e1e079a6a74418a2f30c8169cdf17b61044ee Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 16:20:55 +0100 Subject: [PATCH 139/242] Clarify that the experimental flag is not set for "experimental 0.5.0" --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 2792c5e72e4c..fe4bd7fde83f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,7 +11,7 @@ Features: * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. - * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation. + * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` and do not set the experimental flag in the bytecode for this. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. From cbd8644f2d17d786870fcf94a798ec9e932690db Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Mar 2018 18:15:25 +0100 Subject: [PATCH 140/242] Update changelog for release. --- Changelog.md | 8 ++++---- docs/bugs_by_version.json | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index fe4bd7fde83f..98528893d120 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,22 +1,22 @@ -### 0.4.21 (unreleased) +### 0.4.21 (2018-03-07) Features: - * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. + * General: C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. + * General: Improved messaging when error spans multiple lines of a sourcefile + * General: Support and recommend using ``emit EventName();`` to call events explicitly. * Inline Assembly: Enforce strict mode as experimental 0.5.0 feature. * Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default). * Standard JSON: Reject badly formatted invalid JSON inputs. * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. - * Support and recommend using ``emit EventName();`` to call events explicitly. * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` and do not set the experimental flag in the bytecode for this. * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. * Syntax Checker: Issue warning when using overloads of ``address`` on contract instances. * Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature. - * Improved messaging when error spans multiple lines of a sourcefile Bugfixes: * Assembly: Raise error on oversized number literals in assembly. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 5a4c9e29e88f..4c976a3238c4 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -418,6 +418,10 @@ "bugs": [], "released": "2018-02-14" }, + "0.4.21": { + "bugs": [], + "released": "2018-03-07" + }, "0.4.3": { "bugs": [ "ZeroFunctionSelector", From 16780ea3eda597d0f20975eca88f5fc82febaa89 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Mar 2018 20:26:22 +0100 Subject: [PATCH 141/242] Set version to 0.4.22. --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d4ff1614cee..208e405d9daf 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.21") +set(PROJECT_VERSION "0.4.22") 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 98528893d120..c9bffa64a83e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.4.22 (unreleased) + +Features: + + +Bugfixes: + + + ### 0.4.21 (2018-03-07) Features: From 7ebd580954b355605025a9ef46602382145a2cdc Mon Sep 17 00:00:00 2001 From: Grzegorz Hasse Date: Wed, 7 Mar 2018 21:27:24 -0800 Subject: [PATCH 142/242] Fix a typo. --- 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 4a61d91f0297..4d84a7daee17 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -58,7 +58,7 @@ The following elementary types exist: - ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. -- ``function``: an address (20 bytes) folled by a function selector (4 bytes). Encoded identical to ``bytes24``. +- ``function``: an address (20 bytes) followed by a function selector (4 bytes). Encoded identical to ``bytes24``. The following (fixed-size) array type exists: From 0649f900cacdbe43c127d543c47e834b80210949 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Mar 2018 14:06:54 +0100 Subject: [PATCH 143/242] Properly skip cleanup if only enlarging storage array. --- Changelog.md | 1 + libsolidity/codegen/ArrayUtils.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index c9bffa64a83e..6a25569fa5c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: Bugfixes: + * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index ce8cbb5f2cbc..4703fc1f64c4 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -741,10 +741,10 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const if (_type.isByteArray()) // For a "long" byte array, store length as 2*length+1 _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context<< Instruction::DUP4 << Instruction::SSTORE; + _context << Instruction::DUP4 << Instruction::SSTORE; // skip if size is not reduced _context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::ISZERO << Instruction::GT; + << Instruction::GT << Instruction::ISZERO; _context.appendConditionalJumpTo(resizeEnd); // size reduced, clear the end of the array From a0907e90c6bd33f4788cb2409f233ed054d782c6 Mon Sep 17 00:00:00 2001 From: dongsamb Date: Sat, 10 Mar 2018 11:27:23 +0900 Subject: [PATCH 144/242] Add Korean to Translation --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 184d0e6920ce..8c18c96e4d5e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,6 +37,7 @@ This documentation is translated into several languages by community volunteers, * `Simplified Chinese `_ (in progress) * `Spanish `_ * `Russian `_ (rather outdated) +* `Korean `_ (in progress) Useful links From 413053d6cbb30b5c1e4526c9df4f9889bc678324 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 12 Mar 2018 11:37:44 +0100 Subject: [PATCH 145/242] New release step. --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index b5df9fdac7b6..82b1308c7b10 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -11,6 +11,7 @@ Checklist for making a release: - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems). - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) + - [ ] Update the default version on readthedocs. - [ ] Make a release of ``solc-js``: Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Run ``npm publish`` in the updated ``solc-js`` repository. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. From 7d206ba64ad4590b42aa9ea8d89145aff39c70e0 Mon Sep 17 00:00:00 2001 From: wbt Date: Mon, 12 Mar 2018 10:02:24 -0400 Subject: [PATCH 146/242] Noted `suicide` is deprecated (#3692) According to the [changelog](https://github.com/ethereum/solidity/blob/b5e804b8caba0cc84514898323df91a025705177/Changelog.md), `suicide` was deprecated before 0.4.3 (after 0.2.0) and warning by 0.4.17. --- docs/miscellaneous.rst | 2 +- docs/units-and-global-variables.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 075b6be02beb..a7d5c4456690 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -341,7 +341,7 @@ Global Variables - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address -- ``suicide(address recipient)``: an alias to ``selfdestruct`` +- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct`` - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 1b58b1e86a44..d789e87f3a75 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -183,7 +183,7 @@ Contract Related destroy the current contract, sending its funds to the given :ref:`address` ``suicide(address recipient)``: - alias to ``selfdestruct`` + deprecated alias to ``selfdestruct`` Furthermore, all functions of the current contract are callable directly including the current function. From a59d6d2e5303e714a6e66a5d11bd6a9c2e904e4e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 12 Mar 2018 18:11:08 +0100 Subject: [PATCH 147/242] Support constantinople in evm-version --- Changelog.md | 5 ++--- libsolidity/interface/EVMVersion.h | 2 +- test/libsolidity/StandardCompiler.cpp | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6a25569fa5c8..648af66c612d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,11 +2,10 @@ Features: - Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. - - + * Commandline interface: Support ``--evm-version constantinople`` properly. + * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 13c4ec94da9a..b68e1f4ebdb2 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -49,7 +49,7 @@ class EVMVersion: static boost::optional fromString(std::string const& _version) { - for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()}) + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople()}) if (_version == v.name()) return v; return {}; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 4c8918be88cd..dd6eb7c4005f 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -752,6 +752,8 @@ BOOST_AUTO_TEST_CASE(evm_version) BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos); result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"constantinople\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"constantinople\"") != string::npos); // test default result = compile(inputForVersion("")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); From 121f87b043d7c3f01c760589edf1bf342d67c634 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 14:13:19 +0100 Subject: [PATCH 148/242] Move test tools to the subdirectory test/tools and adjust CMakeLists.txt. --- circle.yml | 2 +- scripts/uniqueErrors.sh | 2 +- test/CMakeLists.txt | 30 ++++++++++++++++++++++++------ test/cmdlineTests.sh | 4 ++-- test/tools/CMakeLists.txt | 2 ++ test/{ => tools}/fuzzer.cpp | 0 6 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test/tools/CMakeLists.txt rename test/{ => tools}/fuzzer.cpp (100%) diff --git a/circle.yml b/circle.yml index 263cb700ea45..49c73ce4dabf 100644 --- a/circle.yml +++ b/circle.yml @@ -126,7 +126,7 @@ jobs: paths: - solc/solc - test/soltest - - test/solfuzzer + - test/tools/solfuzzer test_x86: docker: diff --git a/scripts/uniqueErrors.sh b/scripts/uniqueErrors.sh index eee1df903806..fa2c7b4c028d 100755 --- a/scripts/uniqueErrors.sh +++ b/scripts/uniqueErrors.sh @@ -9,6 +9,6 @@ do echo -n $x " # " # This subshell is a workaround to prevent the shell from printing # "Aborted" - ("$REPO"/build/test/solfuzzer < "$x" || true) 2>&1 | head -n 1 + ("$REPO"/build/test/tools/solfuzzer < "$x" || true) 2>&1 | head -n 1 done ) | sort -u -t'#' -k 2 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f36ad4c532eb..522856cc72e7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,27 @@ -file(GLOB_RECURSE sources "*.cpp") -list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") -file(GLOB_RECURSE headers "*.h") +file(GLOB sources "*.cpp") +file(GLOB headers "*.h") -add_executable(soltest ${sources} ${headers}) +file(GLOB contracts_sources "contracts/*.cpp") +file(GLOB contracts_headers "contracts/*.h") +file(GLOB libdevcore_sources "libdevcore/*.cpp") +file(GLOB libdevcore_headers "libdevcore/*.h") +file(GLOB libevmasm_sources "libevmasm/*.cpp") +file(GLOB libevmasm_headers "libevmasm/*.h") +file(GLOB libjulia_sources "libjulia/*.cpp") +file(GLOB libjulia_headers "libjulia/*.h") +file(GLOB liblll_sources "liblll/*.cpp") +file(GLOB liblll_headers "liblll/*.h") +file(GLOB libsolidity_sources "libsolidity/*.cpp") +file(GLOB libsolidity_headers "libsolidity/*.h") + +add_executable(soltest ${sources} ${headers} + ${contracts_sources} ${contracts_headers} + ${libdevcore_sources} ${libdevcore_headers} + ${libevmasm_sources} ${libevmasm_headers} + ${libjulia_sources} ${libjulia_headers} + ${liblll_sources} ${liblll_headers} + ${libsolidity_sources} ${libsolidity_headers} +) target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) -add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +add_subdirectory(tools) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index e86e0ad485fc..92f9569a1263 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -170,14 +170,14 @@ TMPDIR=$(mktemp -d) for f in *.sol do set +e - "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f" + "$REPO_ROOT"/build/test/tools/solfuzzer --quiet < "$f" if [ $? -ne 0 ]; then printError "Fuzzer failed on:" cat "$f" exit 1 fi - "$REPO_ROOT"/build/test/solfuzzer --without-optimizer --quiet < "$f" + "$REPO_ROOT"/build/test/tools/solfuzzer --without-optimizer --quiet < "$f" if [ $? -ne 0 ]; then printError "Fuzzer (without optimizer) failed on:" cat "$f" diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt new file mode 100644 index 000000000000..a693ebab470b --- /dev/null +++ b/test/tools/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(solfuzzer fuzzer.cpp) +target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/fuzzer.cpp b/test/tools/fuzzer.cpp similarity index 100% rename from test/fuzzer.cpp rename to test/tools/fuzzer.cpp From 6a940f0a99e941c48e5deb695e89ac52784c4f3c Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 21 Jan 2018 13:58:56 +0100 Subject: [PATCH 149/242] [SMTChecker] Support to Bool variables --- libsolidity/formal/SMTChecker.cpp | 2 +- libsolidity/formal/SSAVariable.cpp | 6 +- libsolidity/formal/SSAVariable.h | 2 +- libsolidity/formal/SolverInterface.h | 15 ++-- libsolidity/formal/SymbolicBoolVariable.cpp | 43 +++++++++ libsolidity/formal/SymbolicBoolVariable.h | 47 ++++++++++ test/libsolidity/SMTChecker.cpp | 98 +++++++++++++++++++++ 7 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 libsolidity/formal/SymbolicBoolVariable.cpp create mode 100644 libsolidity/formal/SymbolicBoolVariable.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 1da5b291ea0a..83784ead5c8c 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -444,7 +444,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (_op.annotation().commonType->category() == Type::Category::Integer) + if (SSAVariable::supportedType(_op.annotation().commonType.get())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 4e6bcbcb5cbd..4f8080ab3ae3 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -34,6 +35,8 @@ SSAVariable::SSAVariable( if (dynamic_cast(_decl->type().get())) m_symbolicVar = make_shared(_decl, _interface); + else if (dynamic_cast(_decl->type().get())) + m_symbolicVar = make_shared(_decl, _interface); else { solAssert(false, ""); @@ -42,7 +45,8 @@ SSAVariable::SSAVariable( bool SSAVariable::supportedType(Type const* _decl) { - return dynamic_cast(_decl); + return dynamic_cast(_decl) || + dynamic_cast(_decl); } void SSAVariable::resetIndex() diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 275e85900a1f..d283ad9ea96a 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -68,7 +68,7 @@ class SSAVariable void setZeroValue(); void setUnknownValue(); - /// So far Int is supported. + /// So far Int and Bool are supported. static bool supportedType(Type const* _decl); private: diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 884873100ea5..38d3236eb073 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -46,7 +46,8 @@ enum class Sort { Int, Bool, - IntIntFun // Function of one Int returning a single Int + IntIntFun, // Function of one Int returning a single Int + IntBoolFun // Function of one Int returning a single Bool }; /// C++ representation of an SMTLIB2 expression. @@ -132,10 +133,12 @@ class Expression Expression operator()(Expression _a) const { solAssert( - sort == Sort::IntIntFun && arguments.empty(), + (sort == Sort::IntIntFun || sort == Sort::IntBoolFun) && arguments.empty(), "Attempted function application to non-function." ); - return Expression(name, _a, Sort::Int); + if (sort == Sort::IntIntFun) + return Expression(name, _a, Sort::Int); + return Expression(name, _a, Sort::Bool); } std::string const name; @@ -167,9 +170,11 @@ class SolverInterface virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain) { - solAssert(_domain == Sort::Int && _codomain == Sort::Int, "Function sort not supported."); + solAssert(_domain == Sort::Int && (_codomain == Sort::Int || _codomain == Sort::Bool), "Function sort not supported."); // Subclasses should do something here - return Expression(std::move(_name), {}, Sort::IntIntFun); + if (_codomain == Sort::Int) + return Expression(std::move(_name), {}, Sort::IntIntFun); + return Expression(std::move(_name), {}, Sort::IntBoolFun); } virtual Expression newInteger(std::string _name) { diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp new file mode 100644 index 000000000000..e2a5687db3a7 --- /dev/null +++ b/libsolidity/formal/SymbolicBoolVariable.cpp @@ -0,0 +1,43 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicBoolVariable::SymbolicBoolVariable( + Declaration const* _decl, + smt::SolverInterface&_interface +): + SymbolicVariable(_decl, _interface) +{ + solAssert(m_declaration->type()->category() == Type::Category::Bool, ""); + m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool)); +} + +void SymbolicBoolVariable::setZeroValue(int _seq) +{ + m_interface.addAssertion(valueAtSequence(_seq) == smt::Expression(false)); +} + +void SymbolicBoolVariable::setUnknownValue(int) +{ +} diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h new file mode 100644 index 000000000000..bff56ad0acb3 --- /dev/null +++ b/libsolidity/formal/SymbolicBoolVariable.h @@ -0,0 +1,47 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * Specialization of SymbolicVariable for Bool + */ +class SymbolicBoolVariable: public SymbolicVariable +{ +public: + SymbolicBoolVariable( + Declaration const* _decl, + smt::SolverInterface& _interface + ); + + /// Sets the var to false. + void setZeroValue(int _seq); + /// Does nothing since the SMT solver already knows the valid values. + void setUnknownValue(int _seq); +}; + +} +} diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 12b5f439617a..b97fc80e7ab6 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -329,6 +329,104 @@ BOOST_AUTO_TEST_CASE(ways_to_merge_variables) CHECK_WARNING(text, "Assertion violation happens here"); } +BOOST_AUTO_TEST_CASE(bool_simple) +{ + string text = R"( + contract C { + function f(bool x) public pure { + assert(x); + } + } + )"; + CHECK_WARNING(text, "Assertion violation happens here"); + text = R"( + contract C { + function f(bool x, bool y) public pure { + assert(x == y); + } + } + )"; + CHECK_WARNING(text, "Assertion violation happens here"); + text = R"( + contract C { + function f(bool x, bool y) public pure { + bool z = x || y; + assert(!(x && y) || z); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + if(x) { + assert(x); + } else { + assert(!x); + } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + bool y = x; + assert(x == y); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(bool_int_mixed) +{ + string text = R"( + contract C { + function f(bool x) public pure { + uint a; + if(x) + a = 1; + assert(!x || a > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x, uint a) public pure { + require(!x || a > 0); + uint b = a; + assert(!x || b > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x, bool y) public pure { + uint a; + if (x) { + if (y) { + a = 0; + } else { + a = 1; + } + } else { + if (y) { + a = 1; + } else { + a = 0; + } + } + bool xor_x_y = (x && !y) || (!x && y); + assert(!xor_x_y || a > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(while_loop_simple) { // Check that variables are cleared From c2d26eb6a20a21f5fe4b5d78a39f8c23f7f3f5cb Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 9 Mar 2018 16:19:03 +0100 Subject: [PATCH 150/242] [SMTChecker_Bool] Fix PR comments; Add support to gt, ge, lt, le. and tests. --- libsolidity/formal/SMTChecker.cpp | 48 ++++++++++++++------- libsolidity/formal/SSAVariable.cpp | 21 ++++++--- libsolidity/formal/SSAVariable.h | 6 ++- libsolidity/formal/SolverInterface.h | 29 ++++++++++--- libsolidity/formal/SymbolicBoolVariable.cpp | 4 +- libsolidity/formal/SymbolicBoolVariable.h | 2 +- libsolidity/formal/SymbolicIntVariable.cpp | 6 +-- libsolidity/formal/SymbolicIntVariable.h | 2 +- libsolidity/formal/SymbolicVariable.cpp | 4 +- libsolidity/formal/SymbolicVariable.h | 4 +- test/libsolidity/SMTChecker.cpp | 40 +++++++++++++++++ 11 files changed, 125 insertions(+), 41 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 83784ead5c8c..d4f94f16c33a 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -205,7 +205,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) _assignment.location(), "Assertion checker does not yet implement compound assignment." ); - else if (_assignment.annotation().type->category() != Type::Category::Integer) + else if (!SSAVariable::supportedType(_assignment.annotation().type->category())) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() @@ -266,14 +266,15 @@ void SMTChecker::endVisit(UnaryOperation const& _op) { case Token::Not: // ! { - solAssert(_op.annotation().type->category() == Type::Category::Bool, ""); + solAssert(SSAVariable::typeBool(_op.annotation().type->category()), ""); 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(SSAVariable::typeInteger(_op.annotation().type->category()), ""); solAssert(_op.subExpression().annotation().lValueRequested, ""); if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) { @@ -370,7 +371,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (SSAVariable::supportedType(_identifier.annotation().type.get())) + else if (SSAVariable::supportedType(_identifier.annotation().type->category())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -444,21 +445,36 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (SSAVariable::supportedType(_op.annotation().commonType.get())) + if (SSAVariable::supportedType(_op.annotation().commonType->category())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); - smt::Expression value = ( - op == Token::Equal ? (left == right) : - op == Token::NotEqual ? (left != right) : - op == Token::LessThan ? (left < right) : - op == Token::LessThanOrEqual ? (left <= right) : - op == Token::GreaterThan ? (left > right) : - /*op == Token::GreaterThanOrEqual*/ (left >= right) - ); + shared_ptr value; + if (SSAVariable::typeInteger(_op.annotation().commonType->category())) + { + value = make_shared( + op == Token::Equal ? (left == right) : + op == Token::NotEqual ? (left != right) : + op == Token::LessThan ? (left < right) : + op == Token::LessThanOrEqual ? (left <= right) : + op == Token::GreaterThan ? (left > right) : + /*op == Token::GreaterThanOrEqual*/ (left >= right) + ); + } + else // Bool + { + value = make_shared( + op == Token::Equal ? (left == right) : + op == Token::NotEqual ? (left != right) : + op == Token::LessThan ? (!left && right) : + op == Token::LessThanOrEqual ? (!left || right) : + op == Token::GreaterThan ? (left && !right) : + /*op == Token::GreaterThanOrEqual*/ (left || !right) + ); + } // TODO: check that other values for op are not possible. - defineExpr(_op, value); + defineExpr(_op, *value); } else m_errorReporter.warning( @@ -728,10 +744,10 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (SSAVariable::supportedType(_varDecl.type().get())) + if (SSAVariable::supportedType(_varDecl.type()->category())) { solAssert(m_variables.count(&_varDecl) == 0, ""); - m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); + m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); return true; } else diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 4f8080ab3ae3..3f2a61f1dc3f 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -27,15 +27,15 @@ using namespace dev; using namespace dev::solidity; SSAVariable::SSAVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ) { resetIndex(); - if (dynamic_cast(_decl->type().get())) + if (typeInteger(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); - else if (dynamic_cast(_decl->type().get())) + else if (typeBool(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); else { @@ -43,10 +43,19 @@ SSAVariable::SSAVariable( } } -bool SSAVariable::supportedType(Type const* _decl) +bool SSAVariable::supportedType(Type::Category _category) { - return dynamic_cast(_decl) || - dynamic_cast(_decl); + return typeInteger(_category) || typeBool(_category); +} + +bool SSAVariable::typeInteger(Type::Category _category) +{ + return _category == Type::Category::Integer; +} + +bool SSAVariable::typeBool(Type::Category _category) +{ + return _category == Type::Category::Bool; } void SSAVariable::resetIndex() diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index d283ad9ea96a..7e2ebc8c4f88 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -37,7 +37,7 @@ class SSAVariable /// @param _decl Used to determine the type and forwarded to the symbolic var. /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. SSAVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); @@ -69,7 +69,9 @@ class SSAVariable void setUnknownValue(); /// So far Int and Bool are supported. - static bool supportedType(Type const* _decl); + static bool supportedType(Type::Category _category); + static bool typeInteger(Type::Category _category); + static bool typeBool(Type::Category _category); private: smt::Expression valueAtSequence(int _seq) const diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 38d3236eb073..0bdebb6c7a0b 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -133,12 +133,22 @@ class Expression Expression operator()(Expression _a) const { solAssert( - (sort == Sort::IntIntFun || sort == Sort::IntBoolFun) && arguments.empty(), + arguments.empty(), "Attempted function application to non-function." ); - if (sort == Sort::IntIntFun) + switch (sort) + { + case Sort::IntIntFun: return Expression(name, _a, Sort::Int); - return Expression(name, _a, Sort::Bool); + case Sort::IntBoolFun: + return Expression(name, _a, Sort::Bool); + default: + solAssert( + false, + "Attempted function application to invalid type." + ); + break; + } } std::string const name; @@ -170,11 +180,18 @@ class SolverInterface virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain) { - solAssert(_domain == Sort::Int && (_codomain == Sort::Int || _codomain == Sort::Bool), "Function sort not supported."); + solAssert(_domain == Sort::Int, "Function sort not supported."); // Subclasses should do something here - if (_codomain == Sort::Int) + switch (_codomain) + { + case Sort::Int: return Expression(std::move(_name), {}, Sort::IntIntFun); - return Expression(std::move(_name), {}, Sort::IntBoolFun); + case Sort::Bool: + return Expression(std::move(_name), {}, Sort::IntBoolFun); + default: + solAssert(false, "Function sort not supported."); + break; + } } virtual Expression newInteger(std::string _name) { diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp index e2a5687db3a7..e5c56e4617c9 100644 --- a/libsolidity/formal/SymbolicBoolVariable.cpp +++ b/libsolidity/formal/SymbolicBoolVariable.cpp @@ -24,12 +24,12 @@ using namespace dev; using namespace dev::solidity; SymbolicBoolVariable::SymbolicBoolVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface&_interface ): SymbolicVariable(_decl, _interface) { - solAssert(m_declaration->type()->category() == Type::Category::Bool, ""); + solAssert(m_declaration.type()->category() == Type::Category::Bool, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool)); } diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h index bff56ad0acb3..3510b77030d9 100644 --- a/libsolidity/formal/SymbolicBoolVariable.h +++ b/libsolidity/formal/SymbolicBoolVariable.h @@ -33,7 +33,7 @@ class SymbolicBoolVariable: public SymbolicVariable { public: SymbolicBoolVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index d08dc15537e3..eb7b1c175413 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -24,12 +24,12 @@ using namespace dev; using namespace dev::solidity; SymbolicIntVariable::SymbolicIntVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ): SymbolicVariable(_decl, _interface) { - solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); + solAssert(m_declaration.type()->category() == Type::Category::Integer, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); } @@ -40,7 +40,7 @@ void SymbolicIntVariable::setZeroValue(int _seq) void SymbolicIntVariable::setUnknownValue(int _seq) { - auto const& intType = dynamic_cast(*m_declaration->type()); + auto const& intType = dynamic_cast(*m_declaration.type()); m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); } diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index afa25f1b92d3..eb36b8996a74 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -33,7 +33,7 @@ class SymbolicIntVariable: public SymbolicVariable { public: SymbolicIntVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp index 629049ea51e2..d59b55b10448 100644 --- a/libsolidity/formal/SymbolicVariable.cpp +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -24,7 +24,7 @@ using namespace dev; using namespace dev::solidity; SymbolicVariable::SymbolicVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ): m_declaration(_decl), @@ -34,7 +34,7 @@ SymbolicVariable::SymbolicVariable( string SymbolicVariable::uniqueSymbol() const { - return m_declaration->name() + "_" + to_string(m_declaration->id()); + return m_declaration.name() + "_" + to_string(m_declaration.id()); } diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 9325825036f6..75eb9fa5c2c4 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -37,7 +37,7 @@ class SymbolicVariable { public: SymbolicVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); @@ -60,7 +60,7 @@ class SymbolicVariable return (*m_expression)(_seq); } - Declaration const* m_declaration; + Declaration const& m_declaration; std::shared_ptr m_expression = nullptr; smt::SolverInterface& m_interface; }; diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index b97fc80e7ab6..beb933a4e32c 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -377,6 +377,46 @@ BOOST_AUTO_TEST_CASE(bool_simple) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + require(x); + bool y; + y = false; + assert(x || y); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + bool y; + assert(x <= y); + } + } + )"; + CHECK_WARNING(text, "Assertion violation happens here"); + text = R"( + contract C { + function f(bool x) public pure { + bool y; + assert(x >= y); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + require(x); + bool y; + assert(x > y); + assert(y < x); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(bool_int_mixed) From 9b64dc501da5c743b948e4dca844a6cd67766be1 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 12 Mar 2018 20:15:27 +0100 Subject: [PATCH 151/242] [SMTChecker_Bool] Fix PR review comments: method renaming and solAssert --- libsolidity/formal/SMTChecker.cpp | 15 ++++++++------- libsolidity/formal/SSAVariable.cpp | 12 ++++++------ libsolidity/formal/SSAVariable.h | 6 +++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index d4f94f16c33a..8f4abdc277c7 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -205,7 +205,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) _assignment.location(), "Assertion checker does not yet implement compound assignment." ); - else if (!SSAVariable::supportedType(_assignment.annotation().type->category())) + else if (!SSAVariable::isSupportedType(_assignment.annotation().type->category())) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() @@ -266,7 +266,7 @@ void SMTChecker::endVisit(UnaryOperation const& _op) { case Token::Not: // ! { - solAssert(SSAVariable::typeBool(_op.annotation().type->category()), ""); + solAssert(SSAVariable::isBool(_op.annotation().type->category()), ""); defineExpr(_op, !expr(_op.subExpression())); break; } @@ -274,7 +274,7 @@ void SMTChecker::endVisit(UnaryOperation const& _op) case Token::Dec: // -- (pre- or postfix) { - solAssert(SSAVariable::typeInteger(_op.annotation().type->category()), ""); + solAssert(SSAVariable::isInteger(_op.annotation().type->category()), ""); solAssert(_op.subExpression().annotation().lValueRequested, ""); if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) { @@ -371,7 +371,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (SSAVariable::supportedType(_identifier.annotation().type->category())) + else if (SSAVariable::isSupportedType(_identifier.annotation().type->category())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -445,13 +445,13 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (SSAVariable::supportedType(_op.annotation().commonType->category())) + if (SSAVariable::isSupportedType(_op.annotation().commonType->category())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); shared_ptr value; - if (SSAVariable::typeInteger(_op.annotation().commonType->category())) + if (SSAVariable::isInteger(_op.annotation().commonType->category())) { value = make_shared( op == Token::Equal ? (left == right) : @@ -464,6 +464,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) } else // Bool { + solAssert(SSAVariable::isBool(_op.annotation().commonType->category()), ""); value = make_shared( op == Token::Equal ? (left == right) : op == Token::NotEqual ? (left != right) : @@ -744,7 +745,7 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (SSAVariable::supportedType(_varDecl.type()->category())) + if (SSAVariable::isSupportedType(_varDecl.type()->category())) { solAssert(m_variables.count(&_varDecl) == 0, ""); m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 3f2a61f1dc3f..f3213e03cc5f 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -33,9 +33,9 @@ SSAVariable::SSAVariable( { resetIndex(); - if (typeInteger(_decl.type()->category())) + if (isInteger(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); - else if (typeBool(_decl.type()->category())) + else if (isBool(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); else { @@ -43,17 +43,17 @@ SSAVariable::SSAVariable( } } -bool SSAVariable::supportedType(Type::Category _category) +bool SSAVariable::isSupportedType(Type::Category _category) { - return typeInteger(_category) || typeBool(_category); + return isInteger(_category) || isBool(_category); } -bool SSAVariable::typeInteger(Type::Category _category) +bool SSAVariable::isInteger(Type::Category _category) { return _category == Type::Category::Integer; } -bool SSAVariable::typeBool(Type::Category _category) +bool SSAVariable::isBool(Type::Category _category) { return _category == Type::Category::Bool; } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 7e2ebc8c4f88..bf5dae3b5357 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -69,9 +69,9 @@ class SSAVariable void setUnknownValue(); /// So far Int and Bool are supported. - static bool supportedType(Type::Category _category); - static bool typeInteger(Type::Category _category); - static bool typeBool(Type::Category _category); + static bool isSupportedType(Type::Category _category); + static bool isInteger(Type::Category _category); + static bool isBool(Type::Category _category); private: smt::Expression valueAtSequence(int _seq) const From 49eaf7c3fd98a2983ac7c9f0994fbca0b73a33c1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 20:45:34 +0100 Subject: [PATCH 152/242] Infrastructure for extracting syntax tests in separate test files. --- appveyor.yml | 2 +- scripts/isolate_tests.py | 7 +- scripts/tests.sh | 2 +- test/TestHelper.cpp | 9 ++ test/TestHelper.h | 1 + test/boostTest.cpp | 2 + .../SolidityNameAndTypeResolution.cpp | 52 ------- test/libsolidity/SyntaxTestParser.cpp | 97 +++++++++++++ test/libsolidity/SyntaxTestParser.h | 57 ++++++++ test/libsolidity/SyntaxTester.cpp | 134 ++++++++++++++++++ test/libsolidity/SyntaxTester.h | 48 +++++++ .../double_stateVariable_declaration.sol | 6 + .../double_variable_declaration.sol | 8 ++ .../double_variable_declaration_050.sol | 11 ++ test/libsolidity/syntaxTests/smoke_test.sol | 6 + 15 files changed, 387 insertions(+), 55 deletions(-) create mode 100644 test/libsolidity/SyntaxTestParser.cpp create mode 100644 test/libsolidity/SyntaxTestParser.h create mode 100644 test/libsolidity/SyntaxTester.cpp create mode 100644 test/libsolidity/SyntaxTester.h create mode 100644 test/libsolidity/syntaxTests/double_stateVariable_declaration.sol create mode 100644 test/libsolidity/syntaxTests/double_variable_declaration.sol create mode 100644 test/libsolidity/syntaxTests/double_variable_declaration_050.sol create mode 100644 test/libsolidity/syntaxTests/smoke_test.sol diff --git a/appveyor.yml b/appveyor.yml index ef5f69074c59..5fd85482f060 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - - soltest.exe --show-progress -- --no-ipc --no-smt + - soltest.exe --show-progress -- --testpath %APPVEYOR_BUILD_FOLDER%\test --no-ipc --no-smt # Skip bytecode compare if private key is not available - cd %APPVEYOR_BUILD_FOLDER% - ps: if ($env:priv_key) { diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index cfaef2106ac1..5bf577d3bc0a 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -86,10 +86,15 @@ def write_cases(tests): for root, subdirs, files in os.walk(path): if '_build' in subdirs: subdirs.remove('_build') + if 'compilationTests' in subdirs: + subdirs.remove('compilationTests') for f in files: path = join(root, f) if docs: cases = extract_docs_cases(path) else: - cases = extract_test_cases(path) + if f.endswith(".sol"): + cases = [open(path, "r").read()] + else: + cases = extract_test_cases(path) write_cases(cases) diff --git a/scripts/tests.sh b/scripts/tests.sh index bf4ee3d9d506..37ffafc289e0 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -116,7 +116,7 @@ do log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt fi fi - "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc + "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc done done diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index e0d4423dc0af..77fa204fb693 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -43,6 +43,11 @@ Options::Options() ipcPath = suite.argv[i + 1]; i++; } + else if (string(suite.argv[i]) == "--testpath" && i + 1 < suite.argc) + { + testPath = suite.argv[i + 1]; + i++; + } else if (string(suite.argv[i]) == "--optimize") optimize = true; else if (string(suite.argv[i]) == "--evm-version") @@ -60,6 +65,10 @@ Options::Options() if (!disableIPC && ipcPath.empty()) if (auto path = getenv("ETH_TEST_IPC")) ipcPath = path; + + if (testPath.empty()) + if (auto path = getenv("ETH_TEST_PATH")) + testPath = path; } dev::solidity::EVMVersion Options::evmVersion() const diff --git a/test/TestHelper.h b/test/TestHelper.h index 8c2eec362ecf..9c61be3b9f4f 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,6 +35,7 @@ namespace test struct Options: boost::noncopyable { std::string ipcPath; + std::string testPath; bool showMessages = false; bool optimize = false; bool disableIPC = false; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index a3cc51c5bf24..fa9df3fd63f2 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -36,6 +36,7 @@ #pragma GCC diagnostic pop #include +#include using namespace boost::unit_test; @@ -54,6 +55,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; + dev::solidity::test::SyntaxTester::registerTests(); if (dev::test::Options::get().disableIPC) { for (auto suite: { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 997b610ea265..1f76c01b5bf2 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -43,27 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* text = R"( - contract test { - uint256 stateVariable1; - function fun(uint256 arg1) public { uint256 y; y = arg1; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) -{ - char const* text = R"( - contract test { - uint256 variable; - uint128 variable; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} BOOST_AUTO_TEST_CASE(double_function_declaration) { @@ -76,37 +55,6 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); } -BOOST_AUTO_TEST_CASE(double_variable_declaration) -{ - string text = R"( - contract test { - function f() pure public { - uint256 x; - if (true) { uint256 x; } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - uint256 x; - if (true) { uint256 x; } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "This declaration shadows an existing declaration.", - "Unused local variable", - "Unused local variable" - })); -} - BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) { string text = R"( diff --git a/test/libsolidity/SyntaxTestParser.cpp b/test/libsolidity/SyntaxTestParser.cpp new file mode 100644 index 000000000000..c37caca5329e --- /dev/null +++ b/test/libsolidity/SyntaxTestParser.cpp @@ -0,0 +1,97 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; + +template +void skipWhitespace(IteratorType& it, IteratorType end) +{ + while (it != end && isspace(*it)) + ++it; +} + +template +void skipSlashes(IteratorType& it, IteratorType end) +{ + while (it != end && *it == '/') + ++it; +} + +std::string SyntaxTestParser::parseSource(std::istream& _stream) +{ + std::string source; + string line; + string const delimiter("// ----"); + while (getline(_stream, line)) + if (boost::algorithm::starts_with(line, delimiter)) + break; + else + source += line + "\n"; + return source; +} + +std::vector SyntaxTestParser::parseExpectations(std::istream& _stream) +{ + std::vector expectations; + std::string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + } + return expectations; +} + +SyntaxTest SyntaxTestParser::parse(string const& _filename) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + file.exceptions(ios::badbit); + + SyntaxTest result; + result.source = parseSource(file); + result.expectations = parseExpectations(file); + return result; +} diff --git a/test/libsolidity/SyntaxTestParser.h b/test/libsolidity/SyntaxTestParser.h new file mode 100644 index 000000000000..9e295a0b1c4d --- /dev/null +++ b/test/libsolidity/SyntaxTestParser.h @@ -0,0 +1,57 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +struct SyntaxTestExpectation +{ + std::string type; + std::string message; +}; + +struct SyntaxTest +{ + std::string source; + std::vector expectations; +}; + +class SyntaxTestParser +{ +public: + SyntaxTestParser() = default; + + SyntaxTest parse(std::string const& _filename); +private: + std::string parseSource(std::istream& _stream); + std::vector parseExpectations(std::istream& _stream); +}; + +} +} +} diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp new file mode 100644 index 000000000000..4e5457604bd8 --- /dev/null +++ b/test/libsolidity/SyntaxTester.cpp @@ -0,0 +1,134 @@ +/* + 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 . +*/ + +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; +using namespace boost::unit_test; +namespace fs = boost::filesystem; + +void SyntaxTester::runTest(SyntaxTest const& _test) +{ + vector unexpectedErrors; + auto expectations = _test.expectations; + auto errorList = parseAnalyseAndReturnError(_test.source, true, true, true).second; + + bool errorsMatch = true; + + if (errorList.size() != expectations.size()) + errorsMatch = false; + else + { + for (size_t i = 0; i < errorList.size(); i++) + { + if ( + !(errorList[i]->typeName() == expectations[i].type) || + !(errorMessage(*errorList[i]) == expectations[i].message) + ) + { + errorsMatch = false; + break; + } + } + } + + if (!errorsMatch) + { + string msg = "Test expectation mismatch.\nExpected result:\n"; + if (expectations.empty()) + msg += "\tSuccess\n"; + else + for (auto const& expectation: expectations) + msg += "\t" + expectation.type + ": " + expectation.message + "\n"; + msg += "Obtained result:\n"; + if (errorList.empty()) + msg += "\tSuccess\n"; + else + for (auto const& error: errorList) + msg += "\t" + error->typeName() + ": " + errorMessage(*error) + "\n"; + BOOST_ERROR(msg); + } +} + +std::string SyntaxTester::errorMessage(Error const& _e) +{ + if (_e.comment()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + return "NONE"; +} + +int SyntaxTester::registerTests( + test_suite& _suite, + fs::path const& _basepath, + fs::path const& _path +) +{ + + int numTestsAdded = 0; + fs::path fullpath = _basepath / _path; + if (fs::is_directory(fullpath)) + { + test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); + for (auto const& entry: boost::iterator_range( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + _suite.add(sub_suite); + } + else + { + _suite.add(make_test_case( + [fullpath] { SyntaxTester().runTest(SyntaxTestParser().parse(fullpath.string())); }, + _path.stem().string(), + _path.string(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} + +void SyntaxTester::registerTests() +{ + if(dev::test::Options::get().testPath.empty()) + throw runtime_error( + "No path to the test files was specified. " + "Use the --testpath command line option or " + "the ETH_TEST_PATH environment variable." + ); + auto testPath = fs::path(dev::test::Options::get().testPath); + + if (fs::exists(testPath) && fs::is_directory(testPath)) + { + int numTestsAdded = registerTests( + framework::master_test_suite(), + testPath / "libsolidity", + "syntaxTests" + ); + solAssert(numTestsAdded > 0, "no syntax tests found in libsolidity/syntaxTests"); + } + else + solAssert(false, "libsolidity/syntaxTests directory not found"); +} diff --git a/test/libsolidity/SyntaxTester.h b/test/libsolidity/SyntaxTester.h new file mode 100644 index 000000000000..d61668aa2752 --- /dev/null +++ b/test/libsolidity/SyntaxTester.h @@ -0,0 +1,48 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class SyntaxTester: public AnalysisFramework +{ +public: + static void registerTests(); +private: + static int registerTests( + boost::unit_test::test_suite& _suite, + boost::filesystem::path const& _basepath, + boost::filesystem::path const& _path + ); + static std::string errorMessage(Error const& _e); + void runTest(SyntaxTest const& _test); +}; + +} +} +} diff --git a/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol new file mode 100644 index 000000000000..c5507b64981b --- /dev/null +++ b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol @@ -0,0 +1,6 @@ +contract test { + uint256 variable; + uint128 variable; +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration.sol b/test/libsolidity/syntaxTests/double_variable_declaration.sol new file mode 100644 index 000000000000..3349cfece1e0 --- /dev/null +++ b/test/libsolidity/syntaxTests/double_variable_declaration.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol new file mode 100644 index 000000000000..9c2d40d5204d --- /dev/null +++ b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } +} +// ---- +// Warning: This declaration shadows an existing declaration. +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/smoke_test.sol b/test/libsolidity/syntaxTests/smoke_test.sol new file mode 100644 index 000000000000..2d48098a1d2d --- /dev/null +++ b/test/libsolidity/syntaxTests/smoke_test.sol @@ -0,0 +1,6 @@ +contract test { + uint256 stateVariable1; + function fun(uint256 arg1) public { uint256 y; y = arg1; } +} +// ---- +// Warning: Function state mutability can be restricted to pure From 317c1f7fa36a24259a3c678fad255406df9da64d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 11:03:16 +0100 Subject: [PATCH 153/242] Workaround for boost < 1.59.0 --- test/libsolidity/SyntaxTester.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp index 4e5457604bd8..886bc3bfea24 100644 --- a/test/libsolidity/SyntaxTester.cpp +++ b/test/libsolidity/SyntaxTester.cpp @@ -27,6 +27,19 @@ using namespace std; using namespace boost::unit_test; namespace fs = boost::filesystem; +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function const& _fn, + string const& _name, + string const&, // _filename + size_t // _line +) +{ + return make_test_case(_fn, _name); +} +#endif + + void SyntaxTester::runTest(SyntaxTest const& _test) { vector unexpectedErrors; From 3232561d979954f0625102d33cf042fc5eda7211 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:33:37 +0100 Subject: [PATCH 154/242] Refactoring; fuse SyntaxTestParser and SyntaxTester to SyntaxTest. --- docs/contributing.rst | 15 +- test/TestHelper.h | 2 +- test/boostTest.cpp | 8 +- test/libsolidity/SyntaxTest.cpp | 205 ++++++++++++++++++ .../{SyntaxTester.h => SyntaxTest.h} | 41 +++- test/libsolidity/SyntaxTestParser.cpp | 97 --------- test/libsolidity/SyntaxTestParser.h | 57 ----- test/libsolidity/SyntaxTester.cpp | 147 ------------- 8 files changed, 258 insertions(+), 314 deletions(-) create mode 100644 test/libsolidity/SyntaxTest.cpp rename test/libsolidity/{SyntaxTester.h => SyntaxTest.h} (54%) delete mode 100644 test/libsolidity/SyntaxTestParser.cpp delete mode 100644 test/libsolidity/SyntaxTestParser.h delete mode 100644 test/libsolidity/SyntaxTester.cpp diff --git a/docs/contributing.rst b/docs/contributing.rst index a5efba8bf789..8b4695e40c84 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -69,15 +69,22 @@ Solidity includes different types of tests. They are included in the application called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode, some others require ``libz3`` to be installed. -To disable the z3 tests, use ``./build/test/soltest -- --no-smt`` and -to run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. +``soltest`` reads test contracts that are annotated with expected results +stored in ``./test/libsolidity/syntaxTests``. In order for soltest to find these +tests the root test directory has to be specified using the ``--testpath`` command +line option, e.g. ``./build/test/soltest -- --testpath ./test``. + +To disable the z3 tests, use ``./build/test/soltest -- --no-smt --testpath ./test`` and +to run a subset of the tests that do not require ``cpp-ethereum``, use +``./build/test/soltest -- --no-ipc --testpath ./test``. For all other tests, you need to install `cpp-ethereum `_ and run it in testing mode: ``eth --test -d /tmp/testeth``. -Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc``. +Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``. To run a subset of tests, filters can be used: -``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``. +``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``, +where ``TestName`` can be a wildcard ``*``. Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests and runs ``cpp-ethereum`` automatically if it is in the path (but does not download it). diff --git a/test/TestHelper.h b/test/TestHelper.h index 9c61be3b9f4f..f7b1d94c8eef 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,7 +35,7 @@ namespace test struct Options: boost::noncopyable { std::string ipcPath; - std::string testPath; + boost::filesystem::path testPath; bool showMessages = false; bool optimize = false; bool disableIPC = false; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index fa9df3fd63f2..e557ff95a8ab 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -36,7 +36,7 @@ #pragma GCC diagnostic pop #include -#include +#include using namespace boost::unit_test; @@ -55,7 +55,11 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - dev::solidity::test::SyntaxTester::registerTests(); + solAssert(dev::solidity::test::SyntaxTest::registerTests( + master, + dev::test::Options::get().testPath / "libsolidity", + "syntaxTests" + ) > 0, "no syntax tests found"); if (dev::test::Options::get().disableIPC) { for (auto suite: { diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp new file mode 100644 index 000000000000..8cbe2f36c45b --- /dev/null +++ b/test/libsolidity/SyntaxTest.cpp @@ -0,0 +1,205 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; +namespace fs = boost::filesystem; +using namespace boost::unit_test; + +template +void skipWhitespace(IteratorType& it, IteratorType end) +{ + while (it != end && isspace(*it)) + ++it; +} + +template +void skipSlashes(IteratorType& it, IteratorType end) +{ + while (it != end && *it == '/') + ++it; +} + +SyntaxTest::SyntaxTest(string const& _filename) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + file.exceptions(ios::badbit); + + m_source = parseSource(file); + m_expectations = parseExpectations(file); +} + +bool SyntaxTest::run(ostream& _stream, string const& _indent) +{ + m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; + if (!matchesExpectations(m_errorList)) + { + std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + _stream << _indent << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel); + _stream << _indent << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel); + return false; + } + return true; +} + +void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +{ + if (m_expectations.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& expectation: m_expectations) + _stream << _indent << expectation.type << ": " << expectation.message << endl; +} + +void SyntaxTest::printErrorList( + ostream& _stream, + ErrorList const& _errorList, + string const& _indent +) const +{ + if (_errorList.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& error: _errorList) + _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; +} + +bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const +{ + if (_errorList.size() != m_expectations.size()) + return false; + else + for (size_t i = 0; i < _errorList.size(); i++) + if ( + !(_errorList[i]->typeName() == m_expectations[i].type) || + !(errorMessage(*_errorList[i]) == m_expectations[i].message) + ) + return false; + return true; +} + +string SyntaxTest::errorMessage(Error const& _e) +{ + if (_e.comment()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + return "NONE"; +} + +string SyntaxTest::parseSource(istream& _stream) +{ + string source; + string line; + string const delimiter("// ----"); + while (getline(_stream, line)) + if (boost::algorithm::starts_with(line, delimiter)) + break; + else + source += line + "\n"; + return source; +} + +vector SyntaxTest::parseExpectations(istream& _stream) +{ + vector expectations; + string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + } + return expectations; +} + +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function const& _fn, + string const& _name, + string const& /* _filename */, + size_t /* _line */ +) +{ + return make_test_case(_fn, _name); +} +#endif + +int SyntaxTest::registerTests( + boost::unit_test::test_suite& _suite, + boost::filesystem::path const& _basepath, + boost::filesystem::path const& _path +) +{ + int numTestsAdded = 0; + fs::path fullpath = _basepath / _path; + if (fs::is_directory(fullpath)) + { + test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); + for (auto const& entry: boost::iterator_range( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + _suite.add(sub_suite); + } + else + { + _suite.add(make_test_case( + [fullpath] + { + std::stringstream errorStream; + if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + }, + _path.stem().string(), + _path.string(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} diff --git a/test/libsolidity/SyntaxTester.h b/test/libsolidity/SyntaxTest.h similarity index 54% rename from test/libsolidity/SyntaxTester.h rename to test/libsolidity/SyntaxTest.h index d61668aa2752..4379c77bce81 100644 --- a/test/libsolidity/SyntaxTester.h +++ b/test/libsolidity/SyntaxTest.h @@ -18,10 +18,16 @@ #pragma once #include -#include -#include +#include + +#include #include +#include +#include +#include +#include + namespace dev { namespace solidity @@ -29,18 +35,41 @@ namespace solidity namespace test { -class SyntaxTester: public AnalysisFramework +struct SyntaxTestExpectation +{ + std::string type; + std::string message; +}; + + +class SyntaxTest: AnalysisFramework { public: - static void registerTests(); -private: + SyntaxTest(std::string const& _filename); + + bool run(std::ostream& _stream, std::string const& _indent); + + void printExpected(std::ostream& _stream, std::string const& _indent) const; + void printErrorList( + std::ostream& _stream, + ErrorList const& _errors, + std::string const& _indent + ) const; + static int registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, boost::filesystem::path const& _path ); +private: + bool matchesExpectations(ErrorList const& _errors) const; static std::string errorMessage(Error const& _e); - void runTest(SyntaxTest const& _test); + static std::string parseSource(std::istream& _stream); + static std::vector parseExpectations(std::istream& _stream); + + std::string m_source; + std::vector m_expectations; + ErrorList m_errorList; }; } diff --git a/test/libsolidity/SyntaxTestParser.cpp b/test/libsolidity/SyntaxTestParser.cpp deleted file mode 100644 index c37caca5329e..000000000000 --- a/test/libsolidity/SyntaxTestParser.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - 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 . -*/ - -#include -#include -#include -#include -#include -#include - -using namespace dev; -using namespace solidity; -using namespace dev::solidity::test; -using namespace std; - -template -void skipWhitespace(IteratorType& it, IteratorType end) -{ - while (it != end && isspace(*it)) - ++it; -} - -template -void skipSlashes(IteratorType& it, IteratorType end) -{ - while (it != end && *it == '/') - ++it; -} - -std::string SyntaxTestParser::parseSource(std::istream& _stream) -{ - std::string source; - string line; - string const delimiter("// ----"); - while (getline(_stream, line)) - if (boost::algorithm::starts_with(line, delimiter)) - break; - else - source += line + "\n"; - return source; -} - -std::vector SyntaxTestParser::parseExpectations(std::istream& _stream) -{ - std::vector expectations; - std::string line; - while (getline(_stream, line)) - { - auto it = line.begin(); - - skipSlashes(it, line.end()); - skipWhitespace(it, line.end()); - - if (it == line.end()) continue; - - auto typeBegin = it; - while (it != line.end() && *it != ':') - ++it; - string errorType(typeBegin, it); - - // skip colon - if (it != line.end()) it++; - - skipWhitespace(it, line.end()); - - string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); - } - return expectations; -} - -SyntaxTest SyntaxTestParser::parse(string const& _filename) -{ - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); - file.exceptions(ios::badbit); - - SyntaxTest result; - result.source = parseSource(file); - result.expectations = parseExpectations(file); - return result; -} diff --git a/test/libsolidity/SyntaxTestParser.h b/test/libsolidity/SyntaxTestParser.h deleted file mode 100644 index 9e295a0b1c4d..000000000000 --- a/test/libsolidity/SyntaxTestParser.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - 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 . -*/ - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -struct SyntaxTestExpectation -{ - std::string type; - std::string message; -}; - -struct SyntaxTest -{ - std::string source; - std::vector expectations; -}; - -class SyntaxTestParser -{ -public: - SyntaxTestParser() = default; - - SyntaxTest parse(std::string const& _filename); -private: - std::string parseSource(std::istream& _stream); - std::vector parseExpectations(std::istream& _stream); -}; - -} -} -} diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp deleted file mode 100644 index 886bc3bfea24..000000000000 --- a/test/libsolidity/SyntaxTester.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 . -*/ - -#include -#include -#include -#include - -using namespace dev; -using namespace solidity; -using namespace dev::solidity::test; -using namespace std; -using namespace boost::unit_test; -namespace fs = boost::filesystem; - -#if BOOST_VERSION < 105900 -test_case *make_test_case( - function const& _fn, - string const& _name, - string const&, // _filename - size_t // _line -) -{ - return make_test_case(_fn, _name); -} -#endif - - -void SyntaxTester::runTest(SyntaxTest const& _test) -{ - vector unexpectedErrors; - auto expectations = _test.expectations; - auto errorList = parseAnalyseAndReturnError(_test.source, true, true, true).second; - - bool errorsMatch = true; - - if (errorList.size() != expectations.size()) - errorsMatch = false; - else - { - for (size_t i = 0; i < errorList.size(); i++) - { - if ( - !(errorList[i]->typeName() == expectations[i].type) || - !(errorMessage(*errorList[i]) == expectations[i].message) - ) - { - errorsMatch = false; - break; - } - } - } - - if (!errorsMatch) - { - string msg = "Test expectation mismatch.\nExpected result:\n"; - if (expectations.empty()) - msg += "\tSuccess\n"; - else - for (auto const& expectation: expectations) - msg += "\t" + expectation.type + ": " + expectation.message + "\n"; - msg += "Obtained result:\n"; - if (errorList.empty()) - msg += "\tSuccess\n"; - else - for (auto const& error: errorList) - msg += "\t" + error->typeName() + ": " + errorMessage(*error) + "\n"; - BOOST_ERROR(msg); - } -} - -std::string SyntaxTester::errorMessage(Error const& _e) -{ - if (_e.comment()) - return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); - else - return "NONE"; -} - -int SyntaxTester::registerTests( - test_suite& _suite, - fs::path const& _basepath, - fs::path const& _path -) -{ - - int numTestsAdded = 0; - fs::path fullpath = _basepath / _path; - if (fs::is_directory(fullpath)) - { - test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); - for (auto const& entry: boost::iterator_range( - fs::directory_iterator(fullpath), - fs::directory_iterator() - )) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); - _suite.add(sub_suite); - } - else - { - _suite.add(make_test_case( - [fullpath] { SyntaxTester().runTest(SyntaxTestParser().parse(fullpath.string())); }, - _path.stem().string(), - _path.string(), - 0 - )); - numTestsAdded = 1; - } - return numTestsAdded; -} - -void SyntaxTester::registerTests() -{ - if(dev::test::Options::get().testPath.empty()) - throw runtime_error( - "No path to the test files was specified. " - "Use the --testpath command line option or " - "the ETH_TEST_PATH environment variable." - ); - auto testPath = fs::path(dev::test::Options::get().testPath); - - if (fs::exists(testPath) && fs::is_directory(testPath)) - { - int numTestsAdded = registerTests( - framework::master_test_suite(), - testPath / "libsolidity", - "syntaxTests" - ); - solAssert(numTestsAdded > 0, "no syntax tests found in libsolidity/syntaxTests"); - } - else - solAssert(false, "libsolidity/syntaxTests directory not found"); -} From 7091b6c8b5a91a13ff02255cc0bca08266527e4f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 12:30:56 +0100 Subject: [PATCH 155/242] Minor adjustments. --- test/libsolidity/SyntaxTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 8cbe2f36c45b..f1c60458a3bc 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -48,7 +48,7 @@ SyntaxTest::SyntaxTest(string const& _filename) { ifstream file(_filename); if (!file) - BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); file.exceptions(ios::badbit); m_source = parseSource(file); @@ -60,7 +60,7 @@ bool SyntaxTest::run(ostream& _stream, string const& _indent) m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + std::string nextIndentLevel = _indent + "\t"; _stream << _indent << "Expected result:" << endl; printExpected(_stream, nextIndentLevel); _stream << _indent << "Obtained result:\n"; @@ -99,8 +99,8 @@ bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const else for (size_t i = 0; i < _errorList.size(); i++) if ( - !(_errorList[i]->typeName() == m_expectations[i].type) || - !(errorMessage(*_errorList[i]) == m_expectations[i].message) + (_errorList[i]->typeName() != m_expectations[i].type) || + (errorMessage(*_errorList[i]) != m_expectations[i].message) ) return false; return true; From f56afa21c4c30be51541d99027b96058227da6c4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 12:42:21 +0100 Subject: [PATCH 156/242] Update CODING_STYLE.md --- CODING_STYLE.md | 132 +++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index ee924e726dcd..2cc9ac70993c 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -3,20 +3,22 @@ GOLDEN RULE: Follow the style of the existing code when you make changes. a. Use tabs for leading indentation -- tab stops are every 4 characters. +- tab stops are every 4 characters (only relevant for line length). - One indentation level -> exactly one byte (i.e. a tab character) in the source file. -- If you have run-on lines, indent as you would for a block. b. Line widths: -- Don't worry about having lines of code > 80-char wide. +- Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts. - Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. -c. Don't use braces for condition-body one-liners. +c. Single-statement blocks should not have braces, unless required for clarity. d. Never place condition bodies on same line as condition. -e. Space between first paren and keyword, but *not* following first paren or preceding final paren. -f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. -g. No spaces for subscripting or unary operators. -h. No space before ':' but one after it, except in the ternary operator: one on both sides. -i. Space all other operators. -j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. +e. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis. +f. No spaces for unary operators, `->` or `.`. +g. No space before ':' but one after it, except in the ternary operator: one on both sides. +h. Add spaces around all other operators. +i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. +j. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a + separator (of any kind) are formatted such that there is exactly one element per line, followed by + the separator, the opening parenthesis is on the first line, followed by a line break and the closing + parenthesis is on a line of its own (unindented). See example below. (WRONG) if( a==b[ i ] ) { printf ("Hello\n"); } @@ -25,6 +27,7 @@ foo->bar(someLongVariableName, anotherLongVariableName, anotherLongVariableName, anotherLongVariableName); +cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl; (RIGHT) if (a == b[i]) @@ -36,6 +39,11 @@ foo->bar( anotherLongVariableName, anotherLongVariableName ); +cout << + "some very long string that contains completely irrelevant " << + "text that talks about this and that and contains the words " << + "\"lorem\" and \"ipsum\"" << + endl; @@ -43,7 +51,8 @@ foo->bar( a. No "using namespace" declarations in header files. b. All symbols should be declared in a namespace except for final applications. -c. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. +c. Use anonymous namespaces for helpers whose scope is a cpp file only. +d. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. (WRONG) #include @@ -90,16 +99,16 @@ All other entities' first alpha is lower case. a. Leading underscore "_" to parameter names. - Exception: "o_parameterName" when it is used exclusively for output. See 6(f). - Exception: "io_parameterName" when it is used for both input and output. See 6(f). -b. Leading "c_" to const variables (unless part of an external API). -c. Leading "g_" to global (non-const) variables. -d. Leading "s_" to static (non-const, non-global) variables. +b. Leading "g_" to global (non-const) variables. +c. Leading "s_" to static (non-const, non-global) variables. -5. Error reporting: - -- Prefer exception to bool/int return type. +5. Assertions: +- use `solAssert` and `solUnimplementedAssert` generously to check assumptions + that span across different parts of the code base, for example before dereferencing + a pointer. 6. Declarations: @@ -108,15 +117,14 @@ a. {Typename} + {qualifiers} + {name}. b. Only one per line. c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). d. Favour declarations close to use; don't habitually declare at top of scope ala C. -e. Always pass non-trivial parameters with a const& suffix. -f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. -g. Never use a macro where adequate non-preprocessor C++ can be written. -h. Make use of auto whenever type is clear or unimportant: -- Always avoid doubly-stating the type. -- Use to avoid vast and unimportant type declarations. -- However, avoid using auto where type is not immediately obvious from the context, and especially not for arithmetic expressions. -i. Don't pass bools: prefer enumerations instead. -j. Prefer enum class to straight enum. +e. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move. +f. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments. +g. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer. +h. Never use a macro where adequate non-preprocessor C++ can be written. +i. Only use ``auto`` if the type is very long and rather irrelevant. +j. Do not pass bools: prefer enumerations instead. +k. Prefer enum class to straight enum. +l. Always initialize POD variables, even if their value is overwritten later. (WRONG) @@ -134,13 +142,18 @@ enum class Accuracy Approximate, Exact }; +struct MeanSigma +{ + float mean; + float standardDeviation; +}; double const d = 0; int i; int j; char* s; -std::tuple meanAndSigma(std::vector const& _v, Accuracy _a); -auto x = dynamic_cast(base); -for (auto i = x.begin(); i != x.end(); ++i) {} +MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a); +Derived* x = dynamic_cast(base); +for (auto i = x->begin(); i != x->end(); ++i) {} 7. Structs & classes @@ -169,17 +182,10 @@ f. For a property 'foo' 9. Naming -a. Collection conventions: -- -s means std::vector e.g. using MyTypes = std::vector -- -Set means std::set e.g. using MyTypeSet = std::set -- -Hash means std::unordered_set e.g. using MyTypeHash = std::unordered_set -b. Class conventions: -- -Face means the interface of some shared concept. (e.g. FooFace might be a pure virtual class.) -c. Avoid unpronouncable names; -- If you need to shorten a name favour a pronouncable slice of the original to a scattered set of consonants. -- e.g. Manager shortens to Man rather than Mgr. -d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) -e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +a. Avoid unpronouncable names +b. Names should be shortened only if they are extremely common, but shortening should be generally avoided +c. Avoid prefixes of initials (e.g. do not use IMyInterface, CMyImplementation) +c. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. - A dictionary and thesaurus are your friends. - Spell correctly. - Think carefully about the class's purpose. @@ -194,8 +200,9 @@ a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector; rather than b. Generally avoid shortening a standard form that already includes all important information: - e.g. stick to shared_ptr rather than shortening to ptr. c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. -- e.g. using Guard = std::lock_guard; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly. +- e.g. using Guard = std::lock_guard; ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. +e. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time. @@ -207,40 +214,41 @@ b. Document the interface, not the implementation. - Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). - Be careful to scrutinise documentation that extends only to intended purpose and usage. - Reject documentation that is simply an English transaction of the implementation. - +c. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment. 12. Include Headers -Includes should go in increasing order of generality (libethereum -> libethcore -> libdevcrypto -> libdevcore -> boost -> STL). For example: +Includes should go in increasing order of generality (libsolidity -> libevmasm -> libdevcore -> boost -> STL). +The corresponding .h file should be the first include in the respective .cpp file. +Insert empty lines between blocks of include files. -#include -#include -#include -#include -#include -#include -#include -#include +Example: -See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. +``` +#include + +#include +#include +#include +#include +#include +#include +#include -13. Logging +#include +#include -Logging should be performed at appropriate verbosities depending on the logging message. -The more likely a message is to repeat (and thus cause noise) the higher in verbosity it should be. -Some rules to keep in mind: +#include +#include +``` - - Verbosity == 0 -> Reserved for important stuff that users must see and can understand. - - Verbosity == 1 -> Reserved for stuff that users don't need to see but can understand. - - Verbosity >= 2 -> Anything that is or might be displayed more than once every minute - - Verbosity >= 3 -> Anything that only a developer would understand - - Verbosity >= 4 -> Anything that is low-level (e.g. peer disconnects, timers being cancelled) +See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. -14. Recommended reading +13. Recommended reading Herb Sutter and Bjarne Stroustrup - "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) From 255d2ce95dcdd61e49e1ab458d3e7293a114c6c9 Mon Sep 17 00:00:00 2001 From: Yosyp Schwab Date: Tue, 13 Mar 2018 10:07:10 -0400 Subject: [PATCH 157/242] Updated link for "try Solidity in your browser" Previous link leads to a page with a model warning: _The Remix IDE has moved to http://remix.ethereum.org. This instance of Remix you are visiting WILL NOT BE UPDATED. Please make a backup of your contracts and start using http://remix.ethereum.org_ Updated link to point to http://remix.ethereum.org --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb743729614b..42c392e22b4b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Useful links To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. -You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything. +You can start using [Solidity in your browser](http://remix.ethereum.org) with no need to download or compile anything. The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md). From 069b150e42d12f3f3736dd4af2d82881db244b66 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:44 +0100 Subject: [PATCH 158/242] Bugfix in virtual lookup for modifiers in libraries. --- libsolidity/codegen/CompilerContext.cpp | 14 +++++++++++--- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 5 ++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 0bf88267f05d..47333046673b 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -193,14 +193,22 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } -ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const +ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier( + ModifierDefinition const& _modifier +) const { + // Libraries do not allow inheritance and their functions can be inlined, so we should not + // search the inheritance hierarchy (which will be the wrong one in case the function + // is inlined). + if (auto scope = dynamic_cast(_modifier.scope())) + if (scope->isLibrary()) + return _modifier; solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); for (ContractDefinition const* contract: m_inheritanceHierarchy) for (ModifierDefinition const* modifier: contract->functionModifiers()) - if (modifier->name() == _name) + if (modifier->name() == _modifier.name()) return *modifier; - solAssert(false, "Function modifier " + _name + " not found."); + solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy."); } unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index cf62668331e9..7b6632774f03 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -130,7 +130,7 @@ class CompilerContext void appendMissingLowLevelFunctions(); ABIFunctions& abiFunctions() { return m_abiFunctions; } - ModifierDefinition const& functionModifier(std::string const& _name) const; + ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 5a9498f0e056..95d6c8b5c770 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1002,7 +1002,10 @@ void ContractCompiler::appendModifierOrFunctionCode() appendModifierOrFunctionCode(); else { - ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); + ModifierDefinition const& nonVirtualModifier = dynamic_cast( + *modifierInvocation->name()->annotation().referencedDeclaration + ); + ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); CompilerContext::LocationSetter locationSetter(m_context, modifier); solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); for (unsigned i = 0; i < modifier.parameters().size(); ++i) From 58af150c3dce26206825122d048e4e0732fcb50f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:27 +0100 Subject: [PATCH 159/242] Changelog entry. --- Changelog.md | 1 + .../virtualLookup/modifiers_in_libraries.sol | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol diff --git a/Changelog.md b/Changelog.md index 648af66c612d..2e950f1d76ad 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. + * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol new file mode 100644 index 000000000000..b033fd0c7649 --- /dev/null +++ b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol @@ -0,0 +1,14 @@ +library WithModifier { + modifier mod() { require(msg.value > 10 ether); _; } + function withMod(uint self) mod() internal view { require(self > 0); } +} + +contract Test { + using WithModifier for *; + + function f(uint _value) public payable { + _value.withMod(); + WithModifier.withMod(_value); + } +} +// ---- From 51f9e350b152483968391757155189ed0880c755 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:38 +0100 Subject: [PATCH 160/242] Tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c9ef..282136f742d6 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2879,6 +2879,58 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars) ABI_CHECK(callContractFunction("a()"), encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(function_modifier_library) +{ + char const* sourceCode = R"( + library L { + struct S { uint v; } + modifier mod(S storage s) { s.v++; _; } + function libFun(S storage s) mod(s) internal { s.v += 0x100; } + } + + contract Test { + using L for *; + L.S s; + + function f() public returns (uint) { + s.libFun(); + L.libFun(s); + return s.v; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); +} + +BOOST_AUTO_TEST_CASE(function_modifier_library_inheritance) +{ + // Tests that virtual lookup for modifiers in libraries does not consider + // the current inheritance hierarchy. + + char const* sourceCode = R"( + library L { + struct S { uint v; } + modifier mod(S storage s) { s.v++; _; } + function libFun(S storage s) mod(s) internal { s.v += 0x100; } + } + + contract Test { + using L for *; + L.S s; + modifier mod(L.S storage) { revert(); _; } + + function f() public returns (uint) { + s.libFun(); + L.libFun(s); + return s.v; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); +} + BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) { char const* sourceCode = R"( From c032a7ded1371c67aac2a1721bf522760c4d41a4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 14:21:17 +0100 Subject: [PATCH 161/242] Add soltest.sh script that invokes soltest with the correct --testpath. --- scripts/soltest.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 scripts/soltest.sh diff --git a/scripts/soltest.sh b/scripts/soltest.sh new file mode 100755 index 000000000000..00f484a14372 --- /dev/null +++ b/scripts/soltest.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +USE_DEBUGGER=0 +DEBUGGER="gdb --args" +BOOST_OPTIONS= +SOLTEST_OPTIONS= + +while [ $# -gt 0 ] +do + case "$1" in + --debugger) + shift + DEBUGGER="$1" + USE_DEBUGGER=1 + ;; + --debug) + USE_DEBUGGER=1 + ;; + --boost-options) + shift + BOOST_OPTIONS="${BOOST_OPTIONS} $1" + ;; + -t) + shift + BOOST_OPTIONS="${BOOST_OPTIONS} -t $1" + ;; + --show-progress | -p) + BOOST_OPTIONS="${BOOST_OPTIONS} $1" + ;; + *) + SOLTEST_OPTIONS="${SOLTEST_OPTIONS} $1" + ;; + esac + shift +done +if [ "$USE_DEBUGGER" -ne "0" ]; then + DEBUG_PREFIX=${DEBUGGER} +fi + +exec ${DEBUG_PREFIX} ${REPO_ROOT}/build/test/soltest ${BOOST_OPTIONS} -- --testpath ${REPO_ROOT}/test ${SOLTEST_OPTIONS} From eecc26deec1815191bc3405e54ef84daaba853a1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 17:18:21 +0100 Subject: [PATCH 162/242] Make external library functions accessible. --- Changelog.md | 1 + libsolidity/ast/AST.h | 1 + libsolidity/ast/Types.cpp | 4 ++-- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++++ .../visibility/external_library_function.sol | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/external_library_function.sol diff --git a/Changelog.md b/Changelog.md index 648af66c612d..58b93972e90d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 863ad2fecb53..a25df64b7c53 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -203,6 +203,7 @@ class Declaration: public ASTNode, public Scopable bool isPublic() const { return visibility() >= Visibility::Public; } virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } + bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c08e0e673479..b2881beacd42 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -304,7 +304,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition ); for (FunctionDefinition const* function: library.definedFunctions()) { - if (!function->isVisibleInDerivedContracts() || seenFunctions.count(function)) + if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); FunctionType funType(*function, false); @@ -2875,7 +2875,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current } if (contract.isLibrary()) for (FunctionDefinition const* function: contract.definedFunctions()) - if (function->isVisibleInDerivedContracts()) + if (function->isVisibleAsLibraryMember()) members.push_back(MemberList::Member( function->name(), FunctionType(*function).asMemberFunction(true), diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c9ef..d8c8b8adee4d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6955,6 +6955,21 @@ BOOST_AUTO_TEST_CASE(library_call) ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9)); } +BOOST_AUTO_TEST_CASE(library_function_external) +{ + char const* sourceCode = R"( + library Lib { function m(bytes b) external pure returns (byte) { return b[2]; } } + contract Test { + function f(bytes b) public pure returns (byte) { + return Lib.m(b); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c")); +} + BOOST_AUTO_TEST_CASE(library_stray_values) { char const* sourceCode = R"( diff --git a/test/libsolidity/syntaxTests/visibility/external_library_function.sol b/test/libsolidity/syntaxTests/visibility/external_library_function.sol new file mode 100644 index 000000000000..110e74dbd52b --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/external_library_function.sol @@ -0,0 +1,14 @@ +library L { + function f(uint) pure external {} +} + +contract C { + using L for *; + + function f() public pure { + L.f(2); + uint x; + x.f(); + } +} +// ---- From 834d63de2c4dc9c119862f8bf25b4f7c9f408d6e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 17:28:58 +0100 Subject: [PATCH 163/242] Allow ``block.blockhash`` without being called. --- Changelog.md | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 3 +++ test/libsolidity/SolidityEndToEndTest.cpp | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/Changelog.md b/Changelog.md index 648af66c612d..80c9e8252d3f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: Bugfixes: + * Code Generator: Allow ``block.blockhash`` without being called. * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7162cb0dd28f..f50628ff485b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1147,6 +1147,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "sig") m_context << u256(0) << Instruction::CALLDATALOAD << (u256(0xffffffff) << (256 - 32)) << Instruction::AND; + else if (member == "blockhash") + { + } else solAssert(false, "Unknown magic member."); break; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c9ef..c0ff586d72a5 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1788,6 +1788,23 @@ BOOST_AUTO_TEST_CASE(transfer_ether) ABI_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10), encodeArgs()); } +BOOST_AUTO_TEST_CASE(uncalled_blockhash) +{ + char const* code = R"( + contract C { + function f() public view returns (bytes32) + { + var x = block.blockhash; + return x(block.number - 1); + } + } + )"; + compileAndRun(code, 0, "C"); + bytes result = callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(result.size(), 32); + BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( From 09420f1a4480e8bfeb13f861fda801e49bce8487 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 17:54:22 +0100 Subject: [PATCH 164/242] Store filenames in static variable to guarantee sufficient lifetime. --- test/libsolidity/SyntaxTest.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index f1c60458a3bc..45a851b60086 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace dev; @@ -188,6 +189,9 @@ int SyntaxTest::registerTests( } else { + static vector> filenames; + + filenames.emplace_back(new string(_path.string())); _suite.add(make_test_case( [fullpath] { @@ -196,7 +200,7 @@ int SyntaxTest::registerTests( BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), - _path.string(), + *filenames.back(), 0 )); numTestsAdded = 1; From 0d0c9b868817bad17968f9c23bce5d3844a5971c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 09:55:04 +0100 Subject: [PATCH 165/242] DocStringParser: Fix error message for empty parameter description. --- libsolidity/parsing/DocStringParser.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 0409de7203bc..d058d5569036 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -119,21 +119,17 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) return _end; } auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); - if (nameEndPos == _end) - { - appendError("End of param name not found: " + string(nameStartPos, _end)); - return _end; - } auto paramName = string(nameStartPos, nameEndPos); auto descStartPos = skipWhitespace(nameEndPos, _end); - if (descStartPos == _end) + auto nlPos = find(descStartPos, _end, '\n'); + + if (descStartPos == nlPos) { appendError("No description given for param " + paramName); return _end; } - auto nlPos = find(descStartPos, _end, '\n'); auto paramDesc = string(descStartPos, nlPos); newTag("param"); m_lastTag->paramName = paramName; From 9d079fd1261e40339157bff6fd46de96ae844562 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 10:34:16 +0100 Subject: [PATCH 166/242] DocStringParser: Add Changelog entry and test case for empty descriptions. --- Changelog.md | 1 + .../libsolidity/syntaxTests/docstring_empty_description.sol | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 test/libsolidity/syntaxTests/docstring_empty_description.sol diff --git a/Changelog.md b/Changelog.md index 648af66c612d..a0ad62426fec 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * DocString Parser: Fix error message for empty descriptions. ### 0.4.21 (2018-03-07) diff --git a/test/libsolidity/syntaxTests/docstring_empty_description.sol b/test/libsolidity/syntaxTests/docstring_empty_description.sol new file mode 100644 index 000000000000..0caa1b232069 --- /dev/null +++ b/test/libsolidity/syntaxTests/docstring_empty_description.sol @@ -0,0 +1,6 @@ +contract C { + /// @param id + function vote(uint id) public; +} +// ---- +// DocstringParsingError: No description given for param id From 1882c508c6d3ef46847ba23d06689ea17b01ccb5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 10:45:01 +0100 Subject: [PATCH 167/242] soltest: force the use of the --testpath option for soltest with an explicit error. --- test/boostTest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index e557ff95a8ab..8ad97db3908b 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -55,6 +55,10 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; + solAssert( + !dev::test::Options::get().testPath.empty(), + "No test path specified. The --testpath argument is required." + ); solAssert(dev::solidity::test::SyntaxTest::registerTests( master, dev::test::Options::get().testPath / "libsolidity", From d63d41b3b545b0e13e2ee7f880120b2ba852c654 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 12:04:04 +0100 Subject: [PATCH 168/242] test: Rename test/TestHelper.* to test/Options.* and add Options::validate(). --- test/ExecutionFramework.h | 2 +- test/{TestHelper.cpp => Options.cpp} | 16 +++++++++++++++- test/{TestHelper.h => Options.h} | 1 + test/RPCSession.cpp | 2 +- test/boostTest.cpp | 7 ++----- test/libdevcore/Checksum.cpp | 2 +- test/libdevcore/IndentedWriter.cpp | 2 +- test/libdevcore/JSON.cpp | 2 +- test/libdevcore/StringUtils.cpp | 2 +- test/libdevcore/SwarmHash.cpp | 2 +- test/libdevcore/UTF8.cpp | 2 +- test/libdevcore/Whiskers.cpp | 2 +- test/libevmasm/Optimiser.cpp | 2 +- test/libevmasm/SourceLocation.cpp | 2 +- test/libjulia/Common.cpp | 2 +- test/libjulia/Parser.cpp | 2 +- test/liblll/Compiler.cpp | 2 +- test/liblll/EndToEndTest.cpp | 2 +- test/libsolidity/ASTJSON.cpp | 2 +- test/libsolidity/AnalysisFramework.cpp | 2 +- test/libsolidity/Assembly.cpp | 2 +- test/libsolidity/Imports.cpp | 2 +- test/libsolidity/InlineAssembly.cpp | 2 +- test/libsolidity/JSONCompiler.cpp | 4 ++-- test/libsolidity/Metadata.cpp | 4 ++-- test/libsolidity/SemVerMatcher.cpp | 2 +- test/libsolidity/SolidityABIJSON.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- test/libsolidity/SolidityExpressionCompiler.cpp | 2 +- .../SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/SolidityNatspecJSON.cpp | 2 +- test/libsolidity/SolidityParser.cpp | 4 ++-- test/libsolidity/ViewPureChecker.cpp | 2 +- 33 files changed, 51 insertions(+), 39 deletions(-) rename test/{TestHelper.cpp => Options.cpp} (85%) rename test/{TestHelper.h => Options.h} (98%) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index a7971b8108ee..ee8da3226bd1 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include diff --git a/test/TestHelper.cpp b/test/Options.cpp similarity index 85% rename from test/TestHelper.cpp rename to test/Options.cpp index 77fa204fb693..ff4a7c988997 100644 --- a/test/TestHelper.cpp +++ b/test/Options.cpp @@ -19,9 +19,10 @@ * @date 2014 */ -#include +#include #include +#include #include @@ -71,6 +72,19 @@ Options::Options() testPath = path; } +void Options::validate() const +{ + solAssert( + !dev::test::Options::get().testPath.empty(), + "No test path specified. The --testpath argument is required." + ); + if (!disableIPC) + solAssert( + !dev::test::Options::get().ipcPath.empty(), + "No ipc path specified. The --ipcpath argument is required, unless --no-ipc is used." + ); +} + dev::solidity::EVMVersion Options::evmVersion() const { if (!evmVersionString.empty()) diff --git a/test/TestHelper.h b/test/Options.h similarity index 98% rename from test/TestHelper.h rename to test/Options.h index f7b1d94c8eef..9bc698762337 100644 --- a/test/TestHelper.h +++ b/test/Options.h @@ -41,6 +41,7 @@ struct Options: boost::noncopyable bool disableIPC = false; bool disableSMT = false; + void validate() const; solidity::EVMVersion evmVersion() const; static Options const& get(); diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 54871057f06e..03b1341cb7b9 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 8ad97db3908b..f16973b53ba8 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -35,7 +35,7 @@ #pragma GCC diagnostic pop -#include +#include #include using namespace boost::unit_test; @@ -55,10 +55,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - solAssert( - !dev::test::Options::get().testPath.empty(), - "No test path specified. The --testpath argument is required." - ); + dev::test::Options::get().validate(); solAssert(dev::solidity::test::SyntaxTest::registerTests( master, dev::test::Options::get().testPath / "libsolidity", diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp index 4eedbd999f04..95066b69feeb 100644 --- a/test/libdevcore/Checksum.cpp +++ b/test/libdevcore/Checksum.cpp @@ -22,7 +22,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp index a694aa1b9724..916c99d09426 100644 --- a/test/libdevcore/IndentedWriter.cpp +++ b/test/libdevcore/IndentedWriter.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp index 39d71b42b733..39d958f5f3bd 100644 --- a/test/libdevcore/JSON.cpp +++ b/test/libdevcore/JSON.cpp @@ -21,7 +21,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp index 597457cca625..9ee93d02aec2 100644 --- a/test/libdevcore/StringUtils.cpp +++ b/test/libdevcore/StringUtils.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/SwarmHash.cpp b/test/libdevcore/SwarmHash.cpp index 1ed1da181b0f..913586f89d42 100644 --- a/test/libdevcore/SwarmHash.cpp +++ b/test/libdevcore/SwarmHash.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/UTF8.cpp b/test/libdevcore/UTF8.cpp index 719ada7201d9..6de4570fe29c 100644 --- a/test/libdevcore/UTF8.cpp +++ b/test/libdevcore/UTF8.cpp @@ -21,7 +21,7 @@ #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/Whiskers.cpp b/test/libdevcore/Whiskers.cpp index 84149173e9b8..b12acdd7037f 100644 --- a/test/libdevcore/Whiskers.cpp +++ b/test/libdevcore/Whiskers.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index e6abcb53169a..f630b30486e3 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -20,7 +20,7 @@ * Tests for the Solidity optimizer. */ -#include +#include #include #include diff --git a/test/libevmasm/SourceLocation.cpp b/test/libevmasm/SourceLocation.cpp index 6889b3e67659..764da3cd685c 100644 --- a/test/libevmasm/SourceLocation.cpp +++ b/test/libevmasm/SourceLocation.cpp @@ -22,7 +22,7 @@ #include -#include "../TestHelper.h" +#include namespace dev { diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 41f5d320619f..24519b015655 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index df905dd68fa8..9d66658e1ede 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -19,7 +19,7 @@ * Unit tests for parsing Julia. */ -#include "../TestHelper.h" +#include #include diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index 6c6eae3f96bd..ebdea185892b 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -20,7 +20,7 @@ * Unit tests for the LLL compiler. */ -#include +#include #include diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index e5e70cf8cb70..fd8099f2016b 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 9bf60b6477a3..b44dd331ac0f 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -20,7 +20,7 @@ * Tests for the json ast output. */ -#include +#include #include #include diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 7c335a482f8d..4538757de5b4 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index aff610a485d0..5519ae0d9fde 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,7 +20,7 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include +#include #include #include diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index bc81b3b1c258..1b5dd4a5b697 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index a4dcc4d5fa86..34ca33e3c80d 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -20,7 +20,7 @@ * Unit tests for inline assembly. */ -#include "../TestHelper.h" +#include #include #include diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 285c56049d7e..aed0a370658a 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -25,8 +25,8 @@ #include #include -#include "../Metadata.h" -#include "../TestHelper.h" +#include +#include using namespace std; diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index f1edeeb72a24..808bd1e1595b 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -19,8 +19,8 @@ * Unit tests for the metadata output. */ -#include "../Metadata.h" -#include "../TestHelper.h" +#include +#include #include #include #include diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 08ef5277fb27..07f8fba6257c 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -25,7 +25,7 @@ #include #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 0d471b32f879..107abc26a8e7 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -20,7 +20,7 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#include "../TestHelper.h" +#include #include #include diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c9ef..195ec85b3e90 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 5f044b44c0ff..c8adfc6ed64c 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -30,7 +30,7 @@ #include #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 1f76c01b5bf2..c757037c76ea 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 49a725e04f8d..eeebeb745cdc 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -20,7 +20,7 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#include "../TestHelper.h" +#include #include #include #include diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index f03b30e133bc..4e862f608ad4 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -25,8 +25,8 @@ #include #include #include -#include "../TestHelper.h" -#include "ErrorCheck.h" +#include +#include using namespace std; diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 26ff461c6b98..a6ce6d917e8e 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include From f0c174af8fda32f705e30c95170ef2eb087b729b Mon Sep 17 00:00:00 2001 From: Daniel R Date: Thu, 15 Mar 2018 10:42:59 +0000 Subject: [PATCH 169/242] Fix Typo in changelog.md --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e9a9548db65b..d37bce63d840 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ Features: Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. - * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. + * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. From 269241e9105a3b3014002bf711ade985d87febe4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:57:48 +0100 Subject: [PATCH 170/242] Add formatted printing to SyntaxTest and expand its public interface. --- test/libsolidity/FormattedScope.h | 66 +++++++++++++++++++++++++++++++ test/libsolidity/SyntaxTest.cpp | 56 ++++++++++++++++++++------ test/libsolidity/SyntaxTest.h | 17 ++++++-- 3 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 test/libsolidity/FormattedScope.h diff --git a/test/libsolidity/FormattedScope.h b/test/libsolidity/FormattedScope.h new file mode 100644 index 000000000000..78560848e8d9 --- /dev/null +++ b/test/libsolidity/FormattedScope.h @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace formatting +{ + +static constexpr char const* RESET = "\033[0m"; +static constexpr char const* RED = "\033[1;31m"; +static constexpr char const* GREEN = "\033[1;32m"; +static constexpr char const* YELLOW = "\033[1;33m"; +static constexpr char const* CYAN = "\033[1;36m"; +static constexpr char const* BOLD = "\033[1m"; +static constexpr char const* INVERSE = "\033[7m"; + +} + +class FormattedScope: boost::noncopyable +{ +public: + /// @arg _formatting List of formatting strings (e.g. colors) defined in the formatting namespace. + FormattedScope(std::ostream& _stream, bool const _enabled, std::vector const& _formatting): + m_stream(_stream), m_enabled(_enabled) + { + if (m_enabled) + for (auto const& format: _formatting) + m_stream << format; + } + ~FormattedScope() { if (m_enabled) m_stream << formatting::RESET; } + template + std::ostream& operator<<(T&& _t) { return m_stream << std::forward(_t); } +private: + std::ostream& m_stream; + bool m_enabled; +}; + +} +} +} diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 45a851b60086..70abac48a865 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -27,6 +27,7 @@ using namespace dev; using namespace solidity; using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; using namespace std; namespace fs = boost::filesystem; using namespace boost::unit_test; @@ -56,41 +57,70 @@ SyntaxTest::SyntaxTest(string const& _filename) m_expectations = parseExpectations(file); } -bool SyntaxTest::run(ostream& _stream, string const& _indent) +bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent + "\t"; - _stream << _indent << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel); - _stream << _indent << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel); + std::string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel, _formatted); + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const { if (m_expectations.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) - _stream << _indent << expectation.type << ": " << expectation.message << endl; + FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << + _linePrefix << expectation.type << ": " << expectation.message << endl; } void SyntaxTest::printErrorList( ostream& _stream, ErrorList const& _errorList, - string const& _indent + string const& _linePrefix, + bool const _ignoreWarnings, + bool const _lineNumbers, + bool const _formatted ) const { if (_errorList.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) - _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; + { + bool isWarning = (error->type() == Error::Type::Warning); + if (isWarning && _ignoreWarnings) continue; + + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": " << errorMessage(*error) << endl; + } +} + +int SyntaxTest::offsetToLineNumber(int _location) const +{ + // parseAnalyseAndReturnError(...) prepends a version pragma + _location -= strlen("pragma solidity >=0.0;\n"); + if (_location < 0 || static_cast(_location) >= m_source.size()) + return -1; + else + return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); } bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const @@ -196,7 +226,7 @@ int SyntaxTest::registerTests( [fullpath] { std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + if (!SyntaxTest(fullpath.string()).run(errorStream)) BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 4379c77bce81..441cc4f823c1 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -47,13 +48,22 @@ class SyntaxTest: AnalysisFramework public: SyntaxTest(std::string const& _filename); - bool run(std::ostream& _stream, std::string const& _indent); + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); + + std::vector const& expectations() const { return m_expectations; } + std::string const& source() const { return m_source; } + ErrorList const& errorList() const { return m_errorList; } + ErrorList const& compilerErrors() const { return m_compiler.errors(); } + + void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const; - void printExpected(std::ostream& _stream, std::string const& _indent) const; void printErrorList( std::ostream& _stream, ErrorList const& _errors, - std::string const& _indent + std::string const& _linePrefix, + bool const _ignoreWarnings, + bool const _lineNumbers, + bool const _formatted = false ) const; static int registerTests( @@ -66,6 +76,7 @@ class SyntaxTest: AnalysisFramework static std::string errorMessage(Error const& _e); static std::string parseSource(std::istream& _stream); static std::vector parseExpectations(std::istream& _stream); + int offsetToLineNumber(int _location) const; std::string m_source; std::vector m_expectations; From 7fa892eca93cc1b3fd75eb9341c11f0a471970d9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 19:15:48 +0100 Subject: [PATCH 171/242] Add interactive test tool isoltest. --- libdevcore/CommonIO.cpp | 49 ++++++ libdevcore/CommonIO.h | 3 + scripts/isoltest.sh | 6 + test/tools/CMakeLists.txt | 3 + test/tools/isoltest.cpp | 346 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 407 insertions(+) create mode 100755 scripts/isoltest.sh create mode 100644 test/tools/isoltest.cpp diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 8c7e08f6bfb3..6526baf97f33 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -27,6 +27,7 @@ #if defined(_WIN32) #include #else +#include #include #endif #include @@ -118,3 +119,51 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe } } } + +#if defined(_WIN32) +class DisableConsoleBuffering +{ +public: + DisableConsoleBuffering() + { + m_stdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(m_stdin, &m_oldMode); + SetConsoleMode(m_stdin, m_oldMode & (~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT))); + } + ~DisableConsoleBuffering() + { + SetConsoleMode(m_stdin, m_oldMode); + } +private: + HANDLE m_stdin; + DWORD m_oldMode; +}; +#else +class DisableConsoleBuffering +{ +public: + DisableConsoleBuffering() + { + tcgetattr(0, &m_termios); + m_termios.c_lflag &= ~ICANON; + m_termios.c_lflag &= ~ECHO; + m_termios.c_cc[VMIN] = 1; + m_termios.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &m_termios); + } + ~DisableConsoleBuffering() + { + m_termios.c_lflag |= ICANON; + m_termios.c_lflag |= ECHO; + tcsetattr(0, TCSADRAIN, &m_termios); + } +private: + struct termios m_termios; +}; +#endif + +int dev::readStandardInputChar() +{ + DisableConsoleBuffering disableConsoleBuffering; + return cin.get(); +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 33769ec3d7e9..3ecdb4c3833c 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -37,6 +37,9 @@ std::string readFileAsString(std::string const& _file); /// Retrieve and returns the contents of standard input (until EOF). std::string readStandardInput(); +/// Retrieve and returns a character from standard input (without waiting for EOL). +int readStandardInputChar(); + /// Write the given binary data into the given file, replacing the file if it pre-exists. /// Throws exception on error. /// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in diff --git a/scripts/isoltest.sh b/scripts/isoltest.sh new file mode 100755 index 000000000000..8aed0b3aa67c --- /dev/null +++ b/scripts/isoltest.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +exec ${REPO_ROOT}/build/test/tools/isoltest --testpath ${REPO_ROOT}/test diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index a693ebab470b..febb0c26bc92 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,2 +1,5 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) + +add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp) +target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp new file mode 100644 index 000000000000..668481cf8571 --- /dev/null +++ b/test/tools/isoltest.cpp @@ -0,0 +1,346 @@ +/* + 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 . +*/ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; +using namespace std; +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +struct SyntaxTestStats +{ + int successCount; + int runCount; + operator bool() const { return successCount == runCount; } +}; + +class SyntaxTestTool +{ +public: + SyntaxTestTool(string const& _name, fs::path const& _path, bool _formatted): + m_formatted(_formatted), m_name(_name), m_path(_path) + {} + + enum class Result + { + Success, + Failure, + ParserError, + InputOutputError + }; + + Result process(); + + static SyntaxTestStats processPath( + fs::path const& _basepath, + fs::path const& _path, + bool const _formatted + ); + + static string editor; +private: + enum class Request + { + Skip, + Rerun, + Quit + }; + + Request handleResponse(bool const _parserError); + + void printContract() const; + + bool const m_formatted; + string const m_name; + fs::path const m_path; + unique_ptr m_test; +}; + +string SyntaxTestTool::editor; + +void SyntaxTestTool::printContract() const +{ + stringstream stream(m_test->source()); + string line; + while (getline(stream, line)) + cout << " " << line << endl; + cout << endl; +} + +SyntaxTestTool::Result SyntaxTestTool::process() +{ + bool success; + bool parserError = false; + std::stringstream outputMessages; + + (FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush(); + + try + { + m_test = unique_ptr(new SyntaxTest(m_path.string())); + } + catch (std::exception const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << "cannot read test: " << _e.what() << endl; + return Result::InputOutputError; + } + + try + { + success = m_test->run(outputMessages, " ", m_formatted); + } + catch (...) + { + success = false; + parserError = true; + } + + if (success) + { + FormattedScope(cout, m_formatted, {BOLD, GREEN}) << "OK" << endl; + return Result::Success; + } + else + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << "FAIL" << endl; + + FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; + printContract(); + + if (parserError) + { + cout << " "; + FormattedScope(cout, m_formatted, {INVERSE, RED}) << "Parsing failed:" << endl; + m_test->printErrorList(cout, m_test->compilerErrors(), " ", true, true, m_formatted); + cout << endl; + return Result::ParserError; + } + else + { + cout << outputMessages.str() << endl; + return Result::Failure; + } + } +} + +SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) +{ + if (_parserError) + cout << "(e)dit/(s)kip/(q)uit? "; + else + cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? "; + cout.flush(); + + while (true) + { + switch(readStandardInputChar()) + { + case 's': + cout << endl; + return Request::Skip; + case 'u': + if (_parserError) + break; + else + { + cout << endl; + ofstream file(m_path.string(), ios::trunc); + file << m_test->source(); + file << "// ----" << endl; + if (!m_test->errorList().empty()) + m_test->printErrorList(file, m_test->errorList(), "// ", false, false, false); + return Request::Rerun; + } + case 'e': + cout << endl << endl; + if (system((editor + " \"" + m_path.string() + "\"").c_str())) + cerr << "Error running editor command." << endl << endl; + return Request::Rerun; + case 'q': + cout << endl; + return Request::Quit; + default: + break; + } + } +} + + +SyntaxTestStats SyntaxTestTool::processPath( + fs::path const& _basepath, + fs::path const& _path, + bool const _formatted +) +{ + std::queue paths; + paths.push(_path); + int successCount = 0; + int runCount = 0; + + while (!paths.empty()) + { + auto currentPath = paths.front(); + + fs::path fullpath = _basepath / currentPath; + if (fs::is_directory(fullpath)) + { + paths.pop(); + for (auto const& entry: boost::iterator_range( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + paths.push(currentPath / entry.path().filename()); + } + else + { + SyntaxTestTool testTool(currentPath.string(), fullpath, _formatted); + ++runCount; + auto result = testTool.process(); + + switch(result) + { + case Result::Failure: + case Result::ParserError: + switch(testTool.handleResponse(result == Result::ParserError)) + { + case Request::Quit: + return { successCount, runCount }; + case Request::Rerun: + cout << "Re-running test case..." << endl; + --runCount; + break; + case Request::Skip: + paths.pop(); + break; + } + break; + case Result::Success: + paths.pop(); + ++successCount; + break; + default: + // non-recoverable error; continue with next test case + paths.pop(); + break; + } + } + } + + return { successCount, runCount }; + +} + +int main(int argc, char *argv[]) +{ + if (getenv("EDITOR")) + SyntaxTestTool::editor = getenv("EDITOR"); + + fs::path testPath; + bool formatted = true; + po::options_description options( + R"(isoltest, tool for interactively managing test contracts. +Usage: isoltest [Options] --testpath path +Interactively validates test contracts. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ("help", "Show this help screen.") + ("testpath", po::value(&testPath), "path to test files") + ("no-color", "don't use colors") + ("editor", po::value(&SyntaxTestTool::editor), "editor for opening contracts"); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options); + po::store(cmdLineParser.run(), arguments); + + if (arguments.count("help")) + { + cout << options << endl; + return 0; + } + + if (arguments.count("no-color")) + formatted = false; + + po::notify(arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return 1; + } + + if (testPath.empty()) + { + auto const searchPath = + { + fs::current_path() / ".." / ".." / ".." / "test", + fs::current_path() / ".." / ".." / "test", + fs::current_path() / ".." / "test", + fs::current_path() / "test", + fs::current_path() + }; + for (auto const& basePath : searchPath) + { + fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; + if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) + { + testPath = basePath; + break; + } + } + } + + fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests"; + + if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) + { + auto stats = SyntaxTestTool::processPath(testPath / "libsolidity", "syntaxTests", formatted); + + cout << endl << "Summary: "; + FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << + stats.successCount << "/" << stats.runCount; + cout << " tests successful." << endl; + + return stats ? 0 : 1; + } + else + { + cerr << "Test path not found. Use the --testpath argument." << endl; + return 1; + } +} From 50ad89d369a3ffd90471b2738f2f30491841e197 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 15 Mar 2018 16:27:34 +0100 Subject: [PATCH 172/242] Only colour error type, not error message in isoltest. --- test/libsolidity/SyntaxTest.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 70abac48a865..acfdff294c51 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -78,8 +78,11 @@ void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) + { FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": " << expectation.message << endl; + _linePrefix << expectation.type << ": "; + _stream << expectation.message << endl; + } } void SyntaxTest::printErrorList( @@ -99,17 +102,20 @@ void SyntaxTest::printErrorList( bool isWarning = (error->type() == Error::Type::Warning); if (isWarning && _ignoreWarnings) continue; - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); - _stream << _linePrefix; - if (_lineNumbers) { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": "; } - _stream << error->typeName() << ": " << errorMessage(*error) << endl; + _stream << errorMessage(*error) << endl; } } From ea8d5f8afc8cdd886be70efc8f49163af0675014 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 18:20:06 +0100 Subject: [PATCH 173/242] Use /usr/bin/editor if exists. --- test/tools/isoltest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 668481cf8571..b71f14b92165 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -263,6 +263,8 @@ int main(int argc, char *argv[]) { if (getenv("EDITOR")) SyntaxTestTool::editor = getenv("EDITOR"); + else if (fs::exists("/usr/bin/editor")) + SyntaxTestTool::editor = "/usr/bin/editor"; fs::path testPath; bool formatted = true; From 0a4d4d6c528ef57a9391f7f8edf302489ea54bdb Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 23:06:19 +0100 Subject: [PATCH 174/242] Remove ccache on circle. --- circle.yml | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/circle.yml b/circle.yml index 49c73ce4dabf..2b34b1aa95ea 100644 --- a/circle.yml +++ b/circle.yml @@ -89,20 +89,12 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev + apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev - run: name: Store commit hash and prerelease command: | if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - - restore_cache: - key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} - key: ccache-{{ arch }}-{{ .Branch }}- - key: ccache-{{ arch }}-develop- - key: ccache-{{ arch }}- - - run: - name: Configure ccache - command: ccache -M 200M && ccache -c && ccache -s && ccache -z - run: name: Build command: | @@ -110,14 +102,6 @@ jobs: cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo make -j4 - - run: - name: CCache statistics - command: ccache -s - - save_cache: - key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} - when: always - paths: - - ~/.ccache - store_artifacts: path: build/solc/solc destination: solc From 658955579050c51cc9a714adc5986212796f8196 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 00:46:57 +0100 Subject: [PATCH 175/242] Test extraction tool. --- scripts/extract_test_cases.py | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 scripts/extract_test_cases.py diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py new file mode 100755 index 000000000000..07ef9a96a8b7 --- /dev/null +++ b/scripts/extract_test_cases.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# +# This script reads C++ or RST source files and writes all +# multi-line strings into individual files. +# This can be used to extract the Solidity test cases +# into files for e.g. fuzz testing as +# scripts/isolate_tests.py test/libsolidity/* + +import sys +import re +import os +import hashlib +from os.path import join + +def extract_test_cases(path): + lines = open(path, 'rb').read().splitlines() + + inside = False + delimiter = '' + test = '' + + ctr = 1 + test_name = '' + + for l in lines: + if inside: + if l.strip().endswith(')' + delimiter + '";'): + open('%03d_%s.sol' % (ctr, test_name), 'wb').write(test) + ctr += 1 + inside = False + test = '' + else: + l = re.sub('^\t\t', '', l) + l = l.replace('\t', ' ') + test += l + '\n' + else: + m = re.search(r'BOOST_AUTO_TEST_CASE\(([^(]*)\)', l.strip()) + if m: + test_name = m.group(1) + m = re.search(r'R"([^(]*)\($', l.strip()) + if m: + inside = True + delimiter = m.group(1) + + +if __name__ == '__main__': + path = sys.argv[1] + extract_test_cases(path) + From c9db105ad7c8cb4913d0e0ed859ce4808aae288a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 00:47:12 +0100 Subject: [PATCH 176/242] Extract scoping tests. --- .../SolidityNameAndTypeResolution.cpp | 223 ------------------ .../scoping/double_function_declaration.sol | 6 + ...le_variable_declaration_disjoint_scope.sol | 8 + ...ariable_declaration_disjoint_scope_050.sol | 10 + ..._declaration_disjoint_scope_activation.sol | 8 + ...laration_disjoint_scope_activation_050.sol | 10 + .../syntaxTests/scoping/name_shadowing.sol | 7 + .../syntaxTests/scoping/scoping.sol | 11 + .../scoping/scoping_activation.sol | 9 + .../scoping/scoping_activation_old.sol | 6 + .../syntaxTests/scoping/scoping_for.sol | 8 + .../syntaxTests/scoping/scoping_for2.sol | 7 + .../syntaxTests/scoping/scoping_for3.sol | 11 + .../scoping/scoping_for_decl_in_body.sol | 10 + .../syntaxTests/scoping/scoping_old.sol | 6 + .../syntaxTests/scoping/scoping_self_use.sol | 5 + .../scoping/scoping_self_use_050.sol | 8 + 17 files changed, 130 insertions(+), 223 deletions(-) create mode 100644 test/libsolidity/syntaxTests/scoping/double_function_declaration.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol create mode 100644 test/libsolidity/syntaxTests/scoping/name_shadowing.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_activation.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for2.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for3.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_old.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_self_use.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c757037c76ea..2abc0fc751f6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -43,229 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) - -BOOST_AUTO_TEST_CASE(double_function_declaration) -{ - char const* text = R"( - contract test { - function fun() public { } - function fun() public { } - } - )"; - CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Unused local variable", - "Unused local variable" - })); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Unused local variable", - "Unused local variable" - })); -} -BOOST_AUTO_TEST_CASE(scoping_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 4; - uint256 x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() public { - { - uint256 x; - } - x = 2; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_activation_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_activation) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use) -{ - char const* text = R"( - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use_050) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for2) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++) - x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for3) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - x = 4; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (;; y++){ - uint y = 3; - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(name_shadowing) -{ - char const* text = R"( - contract test { - uint256 variable; - function f() public { uint32 variable; variable = 2; } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(name_references) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol new file mode 100644 index 000000000000..2841fd382711 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol @@ -0,0 +1,6 @@ +contract test { + function fun() public { } + function fun() public { } +} +// ---- +// DeclarationError: Function with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol new file mode 100644 index 000000000000..ea61d0f37238 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol new file mode 100644 index 000000000000..221959635b58 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol new file mode 100644 index 000000000000..6af89c93d5c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol new file mode 100644 index 000000000000..73cddfed545b --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol new file mode 100644 index 000000000000..d16877f95630 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol @@ -0,0 +1,7 @@ +contract test { + uint256 variable; + function f() pure public { uint32 variable; variable = 2; } +} +// ---- +// Warning: This declaration shadows an existing declaration. + diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol new file mode 100644 index 000000000000..f47a3e99ef55 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() public { + { + uint256 x; + } + x = 2; + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol new file mode 100644 index 000000000000..0ed74a00dc9d --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol @@ -0,0 +1,9 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + x = 3; + uint x; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "x"? diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol new file mode 100644 index 000000000000..d893a889a985 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 3; + uint x; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for.sol b/test/libsolidity/syntaxTests/scoping/scoping_for.sol new file mode 100644 index 000000000000..6e5b7095e2eb --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol new file mode 100644 index 000000000000..eb74b8ab44ce --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol new file mode 100644 index 000000000000..9bc7d5695e9e --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol new file mode 100644 index 000000000000..07503983e804 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_old.sol new file mode 100644 index 000000000000..83f6b60ba0c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol new file mode 100644 index 000000000000..9e2c0171b89c --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public { + uint a = a; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol new file mode 100644 index 000000000000..e942020e8470 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + uint a = a; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "a"? From e68c19c47b03eebb9af528bf2b03bb0086484c63 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 16 Mar 2018 11:40:03 +0100 Subject: [PATCH 177/242] Only consider files ending with .sol and not starting with ~ in syntax tests. --- test/libsolidity/SyntaxTest.cpp | 10 +++++++++- test/libsolidity/SyntaxTest.h | 1 + test/tools/isoltest.cpp | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index acfdff294c51..ca0511384286 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -205,6 +205,13 @@ test_case *make_test_case( } #endif +bool SyntaxTest::isTestFilename(boost::filesystem::path const& _filename) +{ + return _filename.extension().string() == ".sol" && + !boost::starts_with(_filename.string(), "~") && + !boost::starts_with(_filename.string(), "."); +} + int SyntaxTest::registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, @@ -220,7 +227,8 @@ int SyntaxTest::registerTests( fs::directory_iterator(fullpath), fs::directory_iterator() )) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + if (fs::is_directory(entry.path()) || isTestFilename(entry.path().filename())) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); _suite.add(sub_suite); } else diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 441cc4f823c1..cb6ee05cdbc2 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -71,6 +71,7 @@ class SyntaxTest: AnalysisFramework boost::filesystem::path const& _basepath, boost::filesystem::path const& _path ); + static bool isTestFilename(boost::filesystem::path const& _filename); private: bool matchesExpectations(ErrorList const& _errors) const; static std::string errorMessage(Error const& _e); diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 668481cf8571..5ad3bfb5d662 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -218,7 +219,8 @@ SyntaxTestStats SyntaxTestTool::processPath( fs::directory_iterator(fullpath), fs::directory_iterator() )) - paths.push(currentPath / entry.path().filename()); + if (fs::is_directory(entry.path()) || SyntaxTest::isTestFilename(entry.path().filename())) + paths.push(currentPath / entry.path().filename()); } else { From 6e730df036f6672e07cf96189a8cf721bf7d0cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20A=2E=20R=C3=A9=20Medina?= Date: Mon, 19 Mar 2018 16:38:20 -0300 Subject: [PATCH 178/242] Fix: Missing payable at function forceOwnerChange forceOwnerChange expects ether, and does not have the payable keyword. --- docs/common-patterns.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 7e09f53468de..c62b5acabbf2 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -194,6 +194,7 @@ restrictions highly readable. function forceOwnerChange(address _newOwner) public + payable costs(200 ether) { owner = _newOwner; From 2cdf44f65cdd15f31293ae2fa78d9996d6219af0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 10:13:58 +0100 Subject: [PATCH 179/242] Move the old ABI decoder code. --- libsolidity/codegen/CompilerUtils.cpp | 99 ++++++++++++++++++++++ libsolidity/codegen/CompilerUtils.h | 5 ++ libsolidity/codegen/ContractCompiler.cpp | 103 +---------------------- libsolidity/codegen/ContractCompiler.h | 4 - 4 files changed, 106 insertions(+), 105 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 533aca5cb688..704d8da8874f 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -159,6 +159,105 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) +{ + // We do not check the calldata size, everything is zero-padded + + if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + { + // Use the new JULIA-based decoding function + auto stackHeightBefore = m_context.stackHeight(); + abiDecodeV2(_typeParameters, _fromMemory); + solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 1, ""); + return; + } + + //@todo this does not yet support nested dynamic arrays + + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. + m_context << Instruction::DUP1; + for (TypePointer const& parameterType: _typeParameters) + { + // stack: v1 v2 ... v(k-1) base_offset current_offset + TypePointer type = parameterType->decodingType(); + solUnimplementedAssert(type, "No decoding type found."); + if (type->category() == Type::Category::Array) + { + auto const& arrayType = dynamic_cast(*type); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + if (_fromMemory) + { + solUnimplementedAssert( + arrayType.baseType()->isValueType(), + "Nested memory arrays not yet implemented here." + ); + // @todo If base type is an array or struct, it is still calldata-style encoded, so + // we would have to convert it like below. + solAssert(arrayType.location() == DataLocation::Memory, ""); + if (arrayType.isDynamicallySized()) + { + // compute data pointer + m_context << Instruction::DUP1 << Instruction::MLOAD; + m_context << Instruction::DUP3 << Instruction::ADD; + m_context << Instruction::SWAP2 << Instruction::SWAP1; + m_context << u256(0x20) << Instruction::ADD; + } + else + { + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; + } + } + else + { + // first load from calldata and potentially convert to memory if arrayType is memory + TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); + if (calldataType->isDynamicallySized()) + { + // put on stack: data_pointer length + loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + // stack: base_offset data_offset next_pointer + m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; + // stack: base_offset next_pointer data_pointer + // retrieve length + loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + // stack: base_offset next_pointer length data_pointer + m_context << Instruction::SWAP2; + // stack: base_offset data_pointer length next_pointer + } + else + { + // leave the pointer on the stack + m_context << Instruction::DUP1; + m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; + } + if (arrayType.location() == DataLocation::Memory) + { + // stack: base_offset calldata_ref [length] next_calldata + // copy to memory + // move calldata type up again + moveIntoStack(calldataType->sizeOnStack()); + convertType(*calldataType, arrayType, false, false, true); + // fetch next pointer again + moveToStackTop(arrayType.sizeOnStack()); + } + // move base_offset up + moveToStackTop(1 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + } + } + else + { + solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + loadFromMemoryDynamic(*type, !_fromMemory, true); + moveToStackTop(1 + type->sizeOnStack()); + m_context << Instruction::SWAP1; + } + // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + } + m_context << Instruction::POP << Instruction::POP; +} + void CompilerUtils::encodeToMemory( TypePointers const& _givenTypes, TypePointers const& _targetTypes, diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 3cde281bf438..9fc97b9e3121 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -88,6 +88,11 @@ class CompilerUtils /// Stack post: (memory_offset+length) void storeInMemoryDynamic(Type const& _type, bool _padToWords = true); + /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers. + /// From memory if @a _fromMemory is true, otherwise from call data. + /// Expects source offset on the stack, which is removed. + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); + /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. /// Removes the values from the stack and leaves the updated memory pointer. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 95d6c8b5c770..480db98ec9fc 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -280,7 +280,7 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) m_context << Instruction::DUP2 << Instruction::ADD; CompilerUtils(m_context).storeFreeMemoryPointer(); // stack: - appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true); + CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true); } _constructor.accept(*this); } @@ -367,7 +367,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac { // Parameter for calldataUnpacker m_context << CompilerUtils::dataStartOffset; - appendCalldataUnpacker(functionType->parameterTypes()); + CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); m_context << returnTag; @@ -382,105 +382,6 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac } } -void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) -{ - // We do not check the calldata size, everything is zero-padded - - if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) - { - // Use the new JULIA-based decoding function - auto stackHeightBefore = m_context.stackHeight(); - CompilerUtils(m_context).abiDecodeV2(_typeParameters, _fromMemory); - solAssert(m_context.stackHeight() - stackHeightBefore == CompilerUtils(m_context).sizeOnStack(_typeParameters) - 1, ""); - return; - } - - //@todo this does not yet support nested dynamic arrays - - // Retain the offset pointer as base_offset, the point from which the data offsets are computed. - m_context << Instruction::DUP1; - for (TypePointer const& parameterType: _typeParameters) - { - // stack: v1 v2 ... v(k-1) base_offset current_offset - TypePointer type = parameterType->decodingType(); - solUnimplementedAssert(type, "No decoding type found."); - if (type->category() == Type::Category::Array) - { - auto const& arrayType = dynamic_cast(*type); - solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); - if (_fromMemory) - { - solUnimplementedAssert( - arrayType.baseType()->isValueType(), - "Nested memory arrays not yet implemented here." - ); - // @todo If base type is an array or struct, it is still calldata-style encoded, so - // we would have to convert it like below. - solAssert(arrayType.location() == DataLocation::Memory, ""); - if (arrayType.isDynamicallySized()) - { - // compute data pointer - m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; - m_context << u256(0x20) << Instruction::ADD; - } - else - { - m_context << Instruction::SWAP1 << Instruction::DUP2; - m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; - } - } - else - { - // first load from calldata and potentially convert to memory if arrayType is memory - TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); - if (calldataType->isDynamicallySized()) - { - // put on stack: data_pointer length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer - // retrieve length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer - m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer - } - else - { - // leave the pointer on the stack - m_context << Instruction::DUP1; - m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; - } - if (arrayType.location() == DataLocation::Memory) - { - // stack: base_offset calldata_ref [length] next_calldata - // copy to memory - // move calldata type up again - CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); - CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true); - // fetch next pointer again - CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); - } - // move base_offset up - CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack()); - m_context << Instruction::SWAP1; - } - } - else - { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); - CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); - CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; - } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset - } - m_context << Instruction::POP << Instruction::POP; -} - void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary) { CompilerUtils utils(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 8559ea587b22..e04a56fb1e65 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -90,10 +90,6 @@ class ContractCompiler: private ASTConstVisitor void appendDelegatecallCheck(); void appendFunctionSelector(ContractDefinition const& _contract); void appendCallValueCheck(); - /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. - /// From memory if @a _fromMemory is true, otherwise from call data. - /// Expects source offset on the stack, which is removed. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); void registerStateVariables(ContractDefinition const& _contract); From 32c94f505901201126000eb12087251f5695acbd Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 17:23:37 +0100 Subject: [PATCH 180/242] Simple size check for old ABI decoder. --- libsolidity/codegen/CompilerUtils.cpp | 117 +++++++++++++++++------ libsolidity/codegen/CompilerUtils.h | 10 +- libsolidity/codegen/ContractCompiler.cpp | 2 + 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 704d8da8874f..d9177312568a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -22,11 +22,14 @@ #include #include -#include #include #include #include +#include + +#include + using namespace std; namespace dev @@ -159,26 +162,37 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } -void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds) { - // We do not check the calldata size, everything is zero-padded - + /// Stack: if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { // Use the new JULIA-based decoding function auto stackHeightBefore = m_context.stackHeight(); abiDecodeV2(_typeParameters, _fromMemory); - solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 1, ""); + solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); return; } //@todo this does not yet support nested dynamic arrays + if (_revertOnOutOfBounds) + { + size_t encodedSize = 0; + for (auto const& t: _typeParameters) + encodedSize += t->decodingType()->calldataEncodedSize(true); + m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); + } + + m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::SWAP1; + /// Stack: + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. m_context << Instruction::DUP1; for (TypePointer const& parameterType: _typeParameters) { - // stack: v1 v2 ... v(k-1) base_offset current_offset + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset TypePointer type = parameterType->decodingType(); solUnimplementedAssert(type, "No decoding type found."); if (type->category() == Type::Category::Array) @@ -198,13 +212,35 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // compute data pointer m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; + if (_revertOnOutOfBounds) + { + // Check that the data pointer is valid and that length times + // item size is still inside the range. + Whiskers templ(R"({ + if gt(ptr, 0x100000000) { revert(0, 0) } + ptr := add(ptr, base_offset) + let array_data_start := add(ptr, 0x20) + if gt(array_data_start, input_end) { revert(0, 0) } + let array_length := mload(ptr) + if or( + gt(array_length, 0x100000000), + gt(add(array_data_start, mul(array_length, )), input_end) + ) { revert(0, 0) } + })"); + templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); + m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); + } + else + m_context << Instruction::DUP3 << Instruction::ADD; + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) + moveIntoStack(3); m_context << u256(0x20) << Instruction::ADD; } else { - m_context << Instruction::SWAP1 << Instruction::DUP2; + // Size has already been checked for this one. + moveIntoStack(2); + m_context << Instruction::DUP3; m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; } } @@ -216,24 +252,43 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // put on stack: data_pointer length loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer + m_context << Instruction::SWAP1; + // stack: input_end base_offset next_pointer data_offset + if (_revertOnOutOfBounds) + m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); + m_context << Instruction::DUP3 << Instruction::ADD; + // stack: input_end base_offset next_pointer array_head_ptr + if (_revertOnOutOfBounds) + m_context.appendInlineAssembly( + "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", + {"input_end", "base_offset", "next_ptr", "array_head_ptr"} + ); // retrieve length loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer + // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer + // stack: input_end base_offset data_pointer array_length next_pointer + if (_revertOnOutOfBounds) + { + unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); + m_context.appendInlineAssembly(R"({ + if or( + gt(array_length, 0x100000000), + gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) + ) { revert(0, 0) } + })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); + } } else { - // leave the pointer on the stack + // size has already been checked + // stack: input_end base_offset data_offset m_context << Instruction::DUP1; m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; } if (arrayType.location() == DataLocation::Memory) { - // stack: base_offset calldata_ref [length] next_calldata + // stack: input_end base_offset calldata_ref [length] next_calldata // copy to memory // move calldata type up again moveIntoStack(calldataType->sizeOnStack()); @@ -241,21 +296,27 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem // fetch next pointer again moveToStackTop(arrayType.sizeOnStack()); } - // move base_offset up - moveToStackTop(1 + arrayType.sizeOnStack()); + // move input_end up + // stack: input_end base_offset calldata_ref [length] next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + // stack: base_offset calldata_ref [length] input_end next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); m_context << Instruction::SWAP1; + // stack: calldata_ref [length] input_end base_offset next_calldata } } else { solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); - moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; + // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset + moveToStackTop(1, type->sizeOnStack()); + moveIntoStack(3, type->sizeOnStack()); } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + // stack: v1 v2 ... v(k-1) v(k) input_end base_offset next_offset } - m_context << Instruction::POP << Instruction::POP; + popStackSlots(3); } void CompilerUtils::encodeToMemory( @@ -420,15 +481,13 @@ void CompilerUtils::abiEncodeV2( void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) { - // stack: + // stack: [stack top] auto ret = m_context.pushNewTag(); + moveIntoStack(2); + // stack: [stack top] + m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; - if (_fromMemory) - // TODO pass correct size for the memory case - m_context << (u256(1) << 63); - else - m_context << Instruction::CALLDATASIZE; - m_context << Instruction::SWAP1; + // stack: string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); m_context.appendJumpTo(m_context.namedTag(decoderName)); m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 9fc97b9e3121..76670d4727f9 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -90,8 +90,12 @@ class CompilerUtils /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. - /// Expects source offset on the stack, which is removed. - void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); + /// Calls revert if @a _revertOnOutOfBounds is true and the supplied size is shorter + /// than the static data requirements or if dynamic data pointers reach outside of the + /// area. Also has a hard cap of 0x100000000 for any given length/offset field. + /// Stack pre: + /// Stack post: ... + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = false); /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. @@ -154,7 +158,7 @@ class CompilerUtils /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true, /// the data is taken from memory instead of from calldata. /// Can allocate memory. - /// Stack pre: + /// Stack pre: /// Stack post: ... void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 480db98ec9fc..791edc65a9d5 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -278,6 +278,7 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) m_context.appendProgramSize(); m_context << Instruction::DUP4 << Instruction::CODECOPY; m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::DUP1; CompilerUtils(m_context).storeFreeMemoryPointer(); // stack: CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true); @@ -367,6 +368,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac { // Parameter for calldataUnpacker m_context << CompilerUtils::dataStartOffset; + m_context << Instruction::DUP1 << Instruction::CALLDATASIZE << Instruction::SUB; CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); From cc2f71e4acede70f6c220d3d0ba407ab73c2024c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 00:40:38 +0100 Subject: [PATCH 181/242] Move dynamic type removal out of the type system. --- libsolidity/analysis/TypeChecker.cpp | 12 ++++++++--- libsolidity/ast/Types.cpp | 23 ++++++++++++---------- libsolidity/ast/Types.h | 3 +++ libsolidity/codegen/ExpressionCompiler.cpp | 17 +++++++++++----- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bebdb9b6ec9c..c8e64c7899c6 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,16 +1551,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.expression().annotation().isPure && functionType->isPure(); + bool allowDynamicTypes = false; // @TODO if (!functionType) { m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); _functionCall.annotation().type = make_shared(); return false; } - else if (functionType->returnParameterTypes().size() == 1) - _functionCall.annotation().type = functionType->returnParameterTypes().front(); + + auto returnTypes = + allowDynamicTypes ? + functionType->returnParameterTypes() : + functionType->returnParameterTypesWithoutDynamicTypes(); + if (returnTypes.size() == 1) + _functionCall.annotation().type = returnTypes.front(); else - _functionCall.annotation().type = make_shared(functionType->returnParameterTypes()); + _functionCall.annotation().type = make_shared(returnTypes); if (auto functionName = dynamic_cast(&_functionCall.expression())) { diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index b2881beacd42..41700e283d29 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2311,6 +2311,18 @@ vector FunctionType::parameterNames() const return vector(m_parameterNames.cbegin() + 1, m_parameterNames.cend()); } +TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const +{ + TypePointers returnParameterTypes = m_returnParameterTypes; + + if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall) + for (auto& param: returnParameterTypes) + if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) + param = make_shared(); + + return returnParameterTypes; +} + TypePointers FunctionType::parameterTypes() const { if (!bound()) @@ -2772,18 +2784,9 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) kind = Kind::DelegateCall; } - TypePointers returnParameterTypes = m_returnParameterTypes; - if (kind != Kind::Internal) - { - // Alter dynamic types to be non-accessible. - for (auto& param: returnParameterTypes) - if (param->isDynamicallySized()) - param = make_shared(); - } - return make_shared( parameterTypes, - returnParameterTypes, + m_returnParameterTypes, m_parameterNames, m_returnParameterNames, kind, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c20a025f39b4..c930dd240713 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -973,6 +973,9 @@ class FunctionType: public Type TypePointers parameterTypes() const; std::vector parameterNames() const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } + /// @returns the list of return parameter types. All dynamically-sized types (this excludes + /// storage pointers) are replaced by InaccessibleDynamicType instances. + TypePointers returnParameterTypesWithoutDynamicTypes() const; std::vector const& returnParameterNames() const { return m_returnParameterNames; } /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f50628ff485b..7d148c0c8d66 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -139,8 +139,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& utils().popStackSlots(paramTypes.size() - 1); } unsigned retSizeOnStack = 0; - solAssert(accessorType.returnParameterTypes().size() >= 1, ""); - auto const& returnTypes = accessorType.returnParameterTypes(); + auto returnTypes = accessorType.returnParameterTypes(); + solAssert(returnTypes.size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType.get())) { // remove offset @@ -1618,15 +1618,22 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.experimentalFeatureActive(ExperimentalFeature::V050) && m_context.evmVersion().hasStaticCall(); + bool allowDynamicTypes = false; // @TODO unsigned retSize = 0; + TypePointers returnTypes; if (returnSuccessCondition) retSize = 0; // return value actually is success condition + else if (allowDynamicTypes) + returnTypes = _functionType.returnParameterTypes(); else - for (auto const& retType: _functionType.returnParameterTypes()) + { + returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); + for (auto const& retType: returnTypes) { solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call."); retSize += retType->calldataEncodedSize(); } + } // Evaluate arguments. TypePointers argumentTypes; @@ -1824,11 +1831,11 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().fetchFreeMemoryPointer(); m_context << Instruction::SUB << Instruction::MLOAD; } - else if (!_functionType.returnParameterTypes().empty()) + else if (!returnTypes.empty()) { utils().fetchFreeMemoryPointer(); bool memoryNeeded = false; - for (auto const& retType: _functionType.returnParameterTypes()) + for (auto const& retType: returnTypes) { utils().loadFromMemoryDynamic(*retType, false, true, true); if (dynamic_cast(retType.get())) From c2709a2d8e53155513fa8002a564e434fce68c68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 11:07:30 +0100 Subject: [PATCH 182/242] Decode dynamic data. --- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/codegen/ABIFunctions.cpp | 3 + libsolidity/codegen/CompilerUtils.cpp | 4 +- libsolidity/codegen/ExpressionCompiler.cpp | 57 ++++++++++++++----- .../SolidityNameAndTypeResolution.cpp | 5 ++ 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c8e64c7899c6..999a2a97d7ce 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,7 +1551,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.expression().annotation().isPure && functionType->isPure(); - bool allowDynamicTypes = false; // @TODO + bool allowDynamicTypes = m_evmVersion.supportsReturndata(); if (!functionType) { m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 00f59065301a..8e8908540900 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -253,6 +253,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) templ("body", w.render()); break; } + case Type::Category::InaccessibleDynamic: + templ("body", "cleaned := 0"); + break; default: solAssert(false, "Cleanup of type " + _type.identifier() + " requested."); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d9177312568a..1bd1103b1174 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -198,7 +198,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast(*type); - solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallyEncoded(), "Nested arrays not yet implemented."); if (_fromMemory) { solUnimplementedAssert( @@ -308,7 +308,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem } else { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + solAssert(!type->isDynamicallyEncoded(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset moveToStackTop(1, type->sizeOnStack()); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7d148c0c8d66..37069c3e7013 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1618,22 +1618,27 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.experimentalFeatureActive(ExperimentalFeature::V050) && m_context.evmVersion().hasStaticCall(); - bool allowDynamicTypes = false; // @TODO + bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); unsigned retSize = 0; TypePointers returnTypes; if (returnSuccessCondition) retSize = 0; // return value actually is success condition - else if (allowDynamicTypes) + else if (haveReturndatacopy) returnTypes = _functionType.returnParameterTypes(); else - { returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); - for (auto const& retType: returnTypes) + + bool dynamicReturnSize = false; + for (auto const& retType: returnTypes) + if (retType->isDynamicallyEncoded()) { - solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call."); - retSize += retType->calldataEncodedSize(); + solAssert(haveReturndatacopy, ""); + dynamicReturnSize = true; + retSize = 0; + break; } - } + else + retSize += retType->calldataEncodedSize(); // Evaluate arguments. TypePointers argumentTypes; @@ -1834,17 +1839,39 @@ void ExpressionCompiler::appendExternalFunctionCall( else if (!returnTypes.empty()) { utils().fetchFreeMemoryPointer(); - bool memoryNeeded = false; - for (auto const& retType: returnTypes) + // Stack: return_data_start + + // The old decoder did not allocate any memory (i.e. did not touch the free + // memory pointer), but kept references to the return data for + // (statically-sized) arrays + bool needToUpdateFreeMemoryPtr = false; + if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + needToUpdateFreeMemoryPtr = true; + else + for (auto const& retType: returnTypes) + if (dynamic_cast(retType.get())) + needToUpdateFreeMemoryPtr = true; + + // Stack: return_data_start + if (dynamicReturnSize) { - utils().loadFromMemoryDynamic(*retType, false, true, true); - if (dynamic_cast(retType.get())) - memoryNeeded = true; + solAssert(haveReturndatacopy, ""); + m_context.appendInlineAssembly("{ returndatacopy(return_data_start, 0, returndatasize()) }", {"return_data_start"}); } - if (memoryNeeded) - utils().storeFreeMemoryPointer(); else - m_context << Instruction::POP; + solAssert(retSize > 0, ""); + // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. + // This ensures it can catch badly formatted input from external calls. + m_context << (haveReturndatacopy ? eth::AssemblyItem(Instruction::RETURNDATASIZE) : u256(retSize)); + // Stack: return_data_start return_data_size + if (needToUpdateFreeMemoryPtr) + m_context.appendInlineAssembly(R"({ + // round size to the next multiple of 32 + let newMem := add(start, and(add(size, 0x1f), not(0x1f))) + mstore(0x40, newMem) + })", {"start", "size"}); + + utils().abiDecode(returnTypes, true, true); } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c757037c76ea..8a020ca6c6fc 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3372,6 +3372,11 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; + m_compiler.setEVMVersion(EVMVersion{}); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } From cc0f70263f41ea288391ef1ad416d70aff4b031e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 15:59:34 +0100 Subject: [PATCH 183/242] Tests for returning dynamic data. --- test/libsolidity/ABIDecoderTests.cpp | 83 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 27 ++++-- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index 15c04b3734f3..beb7b5af68ef 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -786,6 +786,89 @@ BOOST_AUTO_TEST_CASE(complex_struct) } +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_simple) +{ + if (m_evmVersion == EVMVersion::homestead()) + return; + + string sourceCode = R"( + contract C { + function dyn() public returns (bytes) { + return "1234567890123456789012345678901234567890"; + } + function f() public returns (bytes) { + return this.dyn(); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x20, 40, string("1234567890123456789012345678901234567890"))); + ) +} + +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced) +{ + if (m_evmVersion == EVMVersion::homestead()) + return; + + string sourceCode = R"( + contract C { + function dyn() public returns (bytes a, uint b, bytes20[] c, uint d) { + a = "1234567890123456789012345678901234567890"; + b = uint(-1); + c = new bytes20[](4); + c[0] = bytes20(1234); + c[3] = bytes20(6789); + d = 0x1234; + } + function f() public returns (bytes, uint, bytes20[], uint) { + return this.dyn(); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x80, u256(-1), 0xe0, 0x1234, + 40, string("1234567890123456789012345678901234567890"), + 4, u256(1234) << (8 * (32 - 20)), 0, 0, u256(6789) << (8 * (32 - 20)) + )); + ) +} + +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_out_of_range) +{ + string sourceCode = R"( + contract C { + function dyn(uint x) public returns (bytes a) { + assembly { + mstore(0, 0x20) + mstore(0x20, 0x21) + return(0, x) + } + } + function f(uint x) public returns (bool) { + this.dyn(x); + return true; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + if (m_evmVersion == EVMVersion::homestead()) + { + ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs(true)); + ABI_CHECK(callContractFunction("f(uint256)", 0x7f), encodeArgs(true)); + } + else + { + ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs()); + ABI_CHECK(callContractFunction("f(uint256)", 0x61), encodeArgs(true)); + } + ABI_CHECK(callContractFunction("f(uint256)", 0x80), encodeArgs(true)); + ) +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8a020ca6c6fc..50ee2b2e46cb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3372,12 +3372,10 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - m_compiler.setEVMVersion(EVMVersion{}); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); - CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); + if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) + CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); + else + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); } BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) @@ -6392,6 +6390,23 @@ BOOST_AUTO_TEST_CASE(return_structs) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(read_returned_struct) +{ + char const* text = R"( + pragma experimental ABIEncoderV2; + contract A { + struct T { + int x; + int y; + } + function g() public returns (T) { + return this.g(); + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + BOOST_AUTO_TEST_CASE(return_recursive_structs) { char const* text = R"( From c7860a0fba74a4b62dfb93070544230e74ce98ab Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Mar 2018 14:20:30 +0100 Subject: [PATCH 184/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index d37bce63d840..c5577b86ad71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.22 (unreleased) Features: + * General: Support accessing dynamic return data in post-byzantium EVMs. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. From 9d9c0bf908c4b61f72595ffca530d7d3fc4d28c2 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 21 Mar 2018 12:47:08 +0100 Subject: [PATCH 185/242] Updates "How to contribute" Adds detailed description of the new syntax test tool. --- docs/contributing.rst | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index 8b4695e40c84..08c125f987cd 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -91,6 +91,68 @@ Alternatively, there is a testing script at ``scripts/test.sh`` which executes a Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. +Writing and running syntax tests +-------------------------------- + +As mentioned above, syntax tests are stored in individual contracts. These files must contain annotations, stating the expected result(s) of the respective test. +The test suite will compile and check them against the given expectations. + +Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` + +:: + + contract test { + uint256 variable; + uint128 variable; + } + // ---- + // DeclarationError: Identifier already declared. + + +The comments in the contract above are used to describe the expected compiler errors. The state variable ``variable`` was declared twice, which is not allowed. +This will result in a ``DeclarationError`` stating that the identifer was already declared. + +The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows +editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``: + +:: + + contract test { + uint256 variable; + } + // ---- + // DeclarationError: Identifier already declared. + +Running ``./test/isoltest`` again will result in a test failure: + +:: + + syntaxTests/double_stateVariable_declaration.sol: FAIL + Contract: + contract test { + uint256 variable; + } + + Expected result: + DeclarationError: Identifier already declared. + Obtained result: + Success + + +which prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit. +``isoltest`` offers several options for failing tests: + +- edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted. +- update: Updates the contract under test. This will remove the annotation which contains the exception not met and runs this test again. +- skip: Skips the execution of this particular test. +- quit: Quits ``isoltest``. + + +.. note:: + + Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``. + Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually. + Whiskers ======== From b79531bebf67b84fbd2cfe90c1911ec563855afa Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 22 Mar 2018 01:20:50 -0300 Subject: [PATCH 186/242] Improve error message when trying to shift by fractional number --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index c5577b86ad71..11be5b9d172f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 41700e283d29..720215c9008d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -327,7 +327,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT else if (IntegerType const* otherInt = dynamic_cast(&_shiftAmountType)) return !otherInt->isAddress(); else if (RationalNumberType const* otherRat = dynamic_cast(&_shiftAmountType)) - return otherRat->integerType() && !otherRat->integerType()->isSigned(); + return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e46cb..ab6113dd442b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5866,6 +5866,16 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const 4294967296"); } +BOOST_AUTO_TEST_CASE(shift_constant_right_fractional) +{ + char const* text = R"( + contract C { + uint public a = 0x42 >> (1 / 2); + } + )"; + CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and rational_const 1 / 2"); +} + BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack) { char const* text = R"( From ef3595b0001614c19174c61d29d221844ed83a0e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 12:02:56 +0100 Subject: [PATCH 187/242] Allow overriding external functions in interfaces with public in an implementing contract --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 15 +++++++++++++++ .../libsolidity/SolidityNameAndTypeResolution.cpp | 14 ++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/Changelog.md b/Changelog.md index c5577b86ad71..7a4149f3827f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. + * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 999a2a97d7ce..487959c5dfe8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -366,6 +366,16 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } +namespace { + +bool functionIsInInterface(FunctionDefinition const& _function) +{ + return dynamic_cast(_function.scope()) && + dynamic_cast(_function.scope())->contractKind() == ContractDefinition::ContractKind::Interface; +} + +} + void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) { FunctionType functionType(function); @@ -378,7 +388,12 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func function.annotation().superFunction = &super; if (function.visibility() != super.visibility()) + { + // visibility is enforced to be external in interfaces, but a contract can override that with public + if (functionIsInInterface(super) && !functionIsInInterface(function) && function.visibility() == FunctionDefinition::Visibility::Public) + return; overrideError(function, super, "Overriding function visibility differs."); + } else if (function.stateMutability() != super.stateMutability()) overrideError( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e46cb..e30ea8a4acc2 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6784,6 +6784,20 @@ BOOST_AUTO_TEST_CASE(using_interface_complex) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(interface_implement_public_contract) +{ + char const* text = R"( + interface I { + function f() external; + } + contract C is I { + function f() public { + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(warn_about_throw) { char const* text = R"( From 8bae2dba7c9697d129a43c1dde54690f3e37a84a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 14 Mar 2018 16:56:06 +0100 Subject: [PATCH 188/242] Introduce inContractKind helper on FunctionDefinition --- libsolidity/analysis/TypeChecker.cpp | 16 +++++----------- libsolidity/ast/AST.cpp | 7 +++++++ libsolidity/ast/AST.h | 2 ++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 487959c5dfe8..6e287f838eb1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -366,16 +366,6 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } -namespace { - -bool functionIsInInterface(FunctionDefinition const& _function) -{ - return dynamic_cast(_function.scope()) && - dynamic_cast(_function.scope())->contractKind() == ContractDefinition::ContractKind::Interface; -} - -} - void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) { FunctionType functionType(function); @@ -390,7 +380,11 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (function.visibility() != super.visibility()) { // visibility is enforced to be external in interfaces, but a contract can override that with public - if (functionIsInInterface(super) && !functionIsInInterface(function) && function.visibility() == FunctionDefinition::Visibility::Public) + if ( + super.inContractKind() == ContractDefinition::ContractKind::Interface && + function.inContractKind() != ContractDefinition::ContractKind::Interface && + function.visibility() == FunctionDefinition::Visibility::Public + ) return; overrideError(function, super, "Overriding function visibility differs."); } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 27220b1f3723..d8ad009d15c8 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -290,6 +290,13 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const return dynamic_cast(*m_annotation); } +ContractDefinition::ContractKind FunctionDefinition::inContractKind() const +{ + auto contractDef = dynamic_cast(scope()); + solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set."); + return contractDef->contractKind(); +} + shared_ptr FunctionDefinition::functionType(bool _internal) const { if (_internal) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a25df64b7c53..9c67d3549983 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -624,6 +624,8 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature() const; + ContractDefinition::ContractKind inContractKind() const; + virtual TypePointer type() const override; /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. From ed632025fe5995d093472b1e7660087e943f2ca6 Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:23:49 +0100 Subject: [PATCH 189/242] Moves blockhash function to global level. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/ast/Types.cpp | 29 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 34cb61d8c7ca..6a858d36db33 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -38,6 +38,7 @@ m_magicVariables(vector>{ make_shared("addmod", make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), + make_shared("blockhash", make_shared(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)), make_shared("ecrecover", make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), make_shared("keccak256", make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 41700e283d29..f4559edae571 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3002,19 +3002,30 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const { + const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: - return MemberList::MemberMap({ - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)} - }); + { + std::vector members = { + {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + }; + + if (!v050) + { + auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, + FunctionType::Kind::BlockHash, false, StateMutability::View); + + members.emplace_back("blockhash", blockhashFun); + } + return MemberList::MemberMap(members); + } case Kind::Message: return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, From be35a65eb3966965e30b6b582f7722338e558013 Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:24:33 +0100 Subject: [PATCH 190/242] Adds unit tests for moved function. --- test/libsolidity/SolidityEndToEndTest.cpp | 29 +++++++++++++++++++ .../SolidityExpressionCompiler.cpp | 9 ++++-- .../SolidityNameAndTypeResolution.cpp | 12 ++++++++ test/libsolidity/ViewPureChecker.cpp | 4 +-- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 44dc40f73f5c..43d390f56b7a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,6 +1805,35 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } +BOOST_AUTO_TEST_CASE(blockhash_global_level) +{ + char const* sourceCode = R"( + contract Test { + function a() public returns (bytes32) { + return blockhash(0); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(!callContractFunction("a()").empty()); +} + +BOOST_AUTO_TEST_CASE(blockhash_shadow) +{ + char const* sourceCode = R"( + contract Test { + function blockhash(uint256 blockNumber) public returns (bytes32) { + return "abc"; + } + function f() returns (bytes32) { + return blockhash(3); + } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("f()") != encodeArgs("abc")); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index c8adfc6ed64c..90d8265c612a 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -503,12 +503,15 @@ BOOST_AUTO_TEST_CASE(blockhash) char const* sourceCode = R"( contract test { function f() { - block.blockhash(3); + blockhash(3); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {}, - {make_shared("block", make_shared(MagicType::Kind::Block))}); + + auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, + FunctionType::Kind::BlockHash, false, StateMutability::View); + + bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared("blockhash", blockhashFun)}); bytes expectation({byte(Instruction::PUSH1), 0x03, byte(Instruction::BLOCKHASH)}); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e46cb..579fb239650e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8555,6 +8555,18 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } +BOOST_AUTO_TEST_CASE(blockhash_not_available_in_block) +{ + char const* text = R"( + contract Test { + function a() public returns (bytes32) { + return block.blockhash(0); + } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"blockhash\" not found or not visible after argument-dependent lookup in block"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index a6ce6d917e8e..de25cfcfd9e8 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -107,7 +107,6 @@ BOOST_AUTO_TEST_CASE(environment_access) vector view{ "block.coinbase", "block.timestamp", - "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", @@ -118,15 +117,16 @@ BOOST_AUTO_TEST_CASE(environment_access) "tx.origin", "tx.gasprice", "this", + "blockhash(7)", "address(1).balance" }; vector pure{ "msg.data", "msg.data[0]", "msg.sig", - "block.blockhash", // Not evaluating the function "msg", "block", + "blockhash", // Not evaluating the function "tx" }; for (string const& x: view) From bddfa47e770d27005bd6604a24033cf2f632b9ee Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:24:51 +0100 Subject: [PATCH 191/242] Updates docs for blockhash changes. --- docs/miscellaneous.rst | 20 +++++++++++++++++++- docs/units-and-global-variables.rst | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index a7d5c4456690..01154854ceeb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -314,7 +314,7 @@ The following is the order of precedence for operators, listed in order of evalu Global Variables ================ -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. - ``block.coinbase`` (``address``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -331,6 +331,7 @@ Global Variables - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``revert()``: abort execution and revert state changes +- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks - ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments ` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments ` @@ -346,6 +347,23 @@ Global Variables - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +.. note:: + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, + unless you know what you are doing. + + Both the timestamp and the block hash can be influenced by miners to some degree. + Bad actors in the mining community can for example run a casino payout function on a chosen hash + and just retry a different hash if they did not receive any money. + + The current block timestamp must be strictly larger than the timestamp of the last block, + but the only guarantee is that it will be somewhere between the timestamps of two + consecutive blocks in the canonical chain. + +.. note:: + The block hashes are not available for all blocks for scalability reasons. + You can only access the hashes of the most recent 256 blocks, all other + values will be zero. + .. index:: visibility, public, private, external, internal Function Visibility Specifiers diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index d789e87f3a75..2571f20aa2ac 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -52,7 +52,7 @@ namespace and are mainly used to provide information about the blockchain. Block and Transaction Properties -------------------------------- -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks excluding current +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. - ``block.coinbase`` (``address``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -74,7 +74,7 @@ Block and Transaction Properties This includes calls to library functions. .. note:: - Do not rely on ``block.timestamp``, ``now`` and ``block.blockhash`` as a source of randomness, + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, unless you know what you are doing. Both the timestamp and the block hash can be influenced by miners to some degree. From 2c56e530467c088c5096d95422313ca211786eca Mon Sep 17 00:00:00 2001 From: bitshift Date: Wed, 7 Mar 2018 10:48:10 +0100 Subject: [PATCH 192/242] Changes deprecation and adjusts tests. --- libsolidity/analysis/StaticAnalyzer.cpp | 15 +++++++++ libsolidity/ast/Types.cpp | 29 +++++----------- test/libsolidity/SolidityEndToEndTest.cpp | 31 ++++------------- .../SolidityNameAndTypeResolution.cpp | 27 +++++++++++---- test/libsolidity/ViewPureChecker.cpp | 33 ++++++++++++++----- 5 files changed, 76 insertions(+), 59 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d4de219af900..6aee260e73c0 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -142,6 +142,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) + { if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") { if (v050) @@ -155,6 +156,20 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" ); } + if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") + { + if (v050) + m_errorReporter.typeError( + _memberAccess.location(), + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + ); + } + } if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f4559edae571..41700e283d29 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3002,30 +3002,19 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { - const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: - { - std::vector members = { - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)} - }; - - if (!v050) - { - auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, - FunctionType::Kind::BlockHash, false, StateMutability::View); - - members.emplace_back("blockhash", blockhashFun); - } - return MemberList::MemberMap(members); - } + return MemberList::MemberMap({ + {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + }); case Kind::Message: return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 43d390f56b7a..a866e46c35f7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,33 +1805,16 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } -BOOST_AUTO_TEST_CASE(blockhash_global_level) +BOOST_AUTO_TEST_CASE(blockhash_shadow_resolution) { - char const* sourceCode = R"( - contract Test { - function a() public returns (bytes32) { - return blockhash(0); - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(!callContractFunction("a()").empty()); -} - -BOOST_AUTO_TEST_CASE(blockhash_shadow) -{ - char const* sourceCode = R"( - contract Test { - function blockhash(uint256 blockNumber) public returns (bytes32) { - return "abc"; - } - function f() returns (bytes32) { - return blockhash(3); - } + char const* code = R"( + contract C { + function blockhash(uint256 blockNumber) public returns(bytes32) { bytes32 x; return x; } + function f() public returns(bytes32) { return blockhash(3); } } )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("f()") != encodeArgs("abc")); + compileAndRun(code, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); } BOOST_AUTO_TEST_CASE(log0) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 579fb239650e..e4e7b8d82cc4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8555,16 +8555,31 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } -BOOST_AUTO_TEST_CASE(blockhash_not_available_in_block) +BOOST_AUTO_TEST_CASE(blockhash) { - char const* text = R"( - contract Test { - function a() public returns (bytes32) { - return block.blockhash(0); + char const* code = R"( + contract C { + function f() public view returns (bytes32) { + return block.blockhash(3); } } )"; - CHECK_ERROR(text, TypeError, "Member \"blockhash\" not found or not visible after argument-dependent lookup in block"); + CHECK_WARNING(code, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); + + code = R"( + contract C { + function f() public view returns (bytes32) { return blockhash(3); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(code); + + code = R"( + pragma experimental "v0.5.0"; + contract C { + function f() public returns (bytes32) { return block.blockhash(3); } + } + )"; + CHECK_ERROR(code, TypeError, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index de25cfcfd9e8..cd0a0b01ce1b 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; @@ -107,9 +108,11 @@ BOOST_AUTO_TEST_CASE(environment_access) vector view{ "block.coinbase", "block.timestamp", + "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", + "blockhash(7)", "gasleft()", "msg.gas", "msg.value", @@ -117,35 +120,47 @@ BOOST_AUTO_TEST_CASE(environment_access) "tx.origin", "tx.gasprice", "this", - "blockhash(7)", "address(1).balance" }; + // ``block.blockhash`` and ``blockhash`` are tested seperately below because their usage will + // produce warnings that can't be handled in a generic way. vector pure{ "msg.data", "msg.data[0]", "msg.sig", "msg", "block", - "blockhash", // Not evaluating the function "tx" }; for (string const& x: view) { CHECK_ERROR( - "contract C { function f() pure public { var x = " + x + "; x; } }", + "contract C { function f() pure public { " + x + "; } }", TypeError, "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); } for (string const& x: pure) { - CHECK_WARNING_ALLOW_MULTI( - "contract C { function f() view public { var x = " + x + "; x; } }", - (std::vector{ - "Function state mutability can be restricted to pure", - "Use of the \"var\" keyword is deprecated." - })); + CHECK_WARNING( + "contract C { function f() view public { " + x + "; } }", + "Function state mutability can be restricted to pure" + ); } + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { blockhash; } }", + (std::vector{ + "Function state mutability can be restricted to pure", + "Statement has no effect." + })); + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { block.blockhash; } }", + (std::vector{ + "Function state mutability can be restricted to pure", + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + })); } BOOST_AUTO_TEST_CASE(view_error_for_050) From f8f50e14d24a85da2775662698db54a73905bd44 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:17:31 +0100 Subject: [PATCH 193/242] Test that internal functions only used by constructor are not included in runtime context. --- test/libsolidity/SolidityCompiler.cpp | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/libsolidity/SolidityCompiler.cpp diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp new file mode 100644 index 000000000000..e87ab6030fe7 --- /dev/null +++ b/test/libsolidity/SolidityCompiler.cpp @@ -0,0 +1,60 @@ +/* + 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 . + */ +/** + * Unit tests for the compiler itself. + */ + +#include + +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(Compiler, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) +{ + char const* sourceCode = R"( + contract C { + uint x; + function C() { f(); } + function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } + } + )"; + BOOST_REQUIRE(success(sourceCode)); + m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); + bytes const& creationBytecode = m_compiler.object("C").bytecode; + bytes const& runtimeBytecode = m_compiler.runtimeObject("C").bytecode; + BOOST_CHECK(creationBytecode.size() >= 120); + BOOST_CHECK(creationBytecode.size() <= 150); + BOOST_CHECK(runtimeBytecode.size() >= 50); + BOOST_CHECK(runtimeBytecode.size() <= 70); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} From 0a67d616db73c912fd16186f97be6ff2d9447975 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:17:08 +0100 Subject: [PATCH 194/242] Use shortcut for internal function calls to avoid runtime reference. --- libsolidity/codegen/ExpressionCompiler.cpp | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 37069c3e7013..f4ca9dfff118 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -518,7 +518,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments[i]->accept(*this); utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]); } - _functionCall.expression().accept(*this); + + { + bool shortcutTaken = false; + if (auto identifier = dynamic_cast(&_functionCall.expression())) + if (auto functionDef = dynamic_cast(identifier->annotation().referencedDeclaration)) + { + // Do not directly visit the identifier, because this way, we can avoid + // the runtime entry label to be created at the creation time context. + CompilerContext::LocationSetter locationSetter2(m_context, *identifier); + m_context << m_context.functionEntryLabel(m_context.resolveVirtualFunction(*functionDef)).pushTag(); + if (m_context.runtimeContext()) + utils().leftShiftNumberOnStack(32); + shortcutTaken = true; + } + + if (!shortcutTaken) + _functionCall.expression().accept(*this); + } + unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes()); if (function.bound()) { @@ -1359,6 +1377,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) + // If the identifier is called right away, this code is executed in visit(FunctionCall...), because + // we want to avoid having a reference to the runtime function entry point in the + // constructor context, since this would force the compiler to include unreferenced + // internal functions in the runtime contex. utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); From 0a58e57ceb2af6b7369742152ea3c4332e7585fb Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:18:47 +0100 Subject: [PATCH 195/242] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c5577b86ad71..aa1554f59093 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. + * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. From fab527c4146f08971938c63074c8c92e6ff241bc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 03:39:37 +0100 Subject: [PATCH 196/242] Add runtimeOnly option to pushCombinedFunctionEntryLabel --- libsolidity/codegen/CompilerUtils.cpp | 9 +++++---- libsolidity/codegen/CompilerUtils.h | 3 ++- libsolidity/codegen/ExpressionCompiler.cpp | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 1bd1103b1174..68f0b3a1192a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -585,7 +585,7 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned) leftShiftNumberOnStack(64); } -void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) +void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly) { m_context << m_context.functionEntryLabel(_function).pushTag(); // If there is a runtime context, we have to merge both labels into the same @@ -593,9 +593,10 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) if (CompilerContext* rtc = m_context.runtimeContext()) { leftShiftNumberOnStack(32); - m_context << - rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << - Instruction::OR; + if (_runtimeOnly) + m_context << + rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << + Instruction::OR; } } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 76670d4727f9..389673ef06ea 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -188,7 +188,8 @@ class CompilerUtils /// Appends code that combines the construction-time (if available) and runtime function /// entry label of the given function into a single stack slot. /// Note: This might cause the compilation queue of the runtime context to be extended. - void pushCombinedFunctionEntryLabel(Declaration const& _function); + /// If @a _runtimeOnly, the entry label will include the runtime assembly tag. + void pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly = true); /// Appends code for an implicit or explicit type conversion. This includes erasing higher /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f4ca9dfff118..9e2d30d5e399 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -527,9 +527,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); - m_context << m_context.functionEntryLabel(m_context.resolveVirtualFunction(*functionDef)).pushTag(); - if (m_context.runtimeContext()) - utils().leftShiftNumberOnStack(32); + utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); shortcutTaken = true; } From 80458b0420b162d0cda889719e8cd645aa142a68 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Tue, 27 Mar 2018 11:55:21 +0200 Subject: [PATCH 197/242] Explains test structure and update mechanism more detailed. --- docs/contributing.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 08c125f987cd..1bcaed7c0969 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -108,9 +108,10 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` // ---- // DeclarationError: Identifier already declared. +A syntax test must contain at least the contract under test itself, followed by the seperator ``----``. The additional comments above are used to describe the +expected compiler errors or warnings. This section can be empty in case that the contract should compile without any errors or warnings. -The comments in the contract above are used to describe the expected compiler errors. The state variable ``variable`` was declared twice, which is not allowed. -This will result in a ``DeclarationError`` stating that the identifer was already declared. +In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifer was already declared. The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``: @@ -143,10 +144,26 @@ which prints the expected result next to the obtained result, but also provides ``isoltest`` offers several options for failing tests: - edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted. -- update: Updates the contract under test. This will remove the annotation which contains the exception not met and runs this test again. +- update: Updates the contract under test. This will either remove the annotation which contains the exception not met or will add missing expectations. The test will then be run again. - skip: Skips the execution of this particular test. - quit: Quits ``isoltest``. +Automatically updating the test above will change it to + +:: + + contract test { + uint256 variable; + } + // ---- + +and re-run the test. It will now pass again: + +:: + + Re-running test case... + syntaxTests/double_stateVariable_declaration.sol: OK + .. note:: From b540ba527a70df440299de9c0bf44f9d11ac6ef6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 14:38:28 +0100 Subject: [PATCH 198/242] Disallow empty structs --- Changelog.md | 1 + docs/grammar.txt | 2 +- libsolidity/analysis/SyntaxChecker.cpp | 7 +++++++ libsolidity/analysis/SyntaxChecker.h | 2 ++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/syntaxTests/empty_struct.sol | 5 +++++ 6 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/empty_struct.sol diff --git a/Changelog.md b/Changelog.md index 9235ed3a2629..3fa0c48563b6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Syntax Checker: Issue error for empty structs. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/docs/grammar.txt b/docs/grammar.txt index a5c2acf35262..b4ca5ca9471c 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -19,7 +19,7 @@ InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';' UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}' + ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}' ModifierDefinition = 'modifier' Identifier ParameterList? Block ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index ddac194b9b1d..e03417a957e0 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -255,3 +255,10 @@ bool SyntaxChecker::visit(VariableDeclaration const& _declaration) } return true; } + +bool SyntaxChecker::visit(StructDefinition const& _struct) +{ + if (_struct.members().empty()) + m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + return true; +} diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 871bf0a98437..1579df57b0ec 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -71,6 +71,8 @@ class SyntaxChecker: private ASTConstVisitor virtual bool visit(VariableDeclaration const& _declaration) override; + virtual bool visit(StructDefinition const& _struct) override; + ErrorReporter& m_errorReporter; /// Flag that indicates whether a function modifier actually contains '_'. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c5abac03cf25..6163aed0ba76 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7279,7 +7279,7 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer) { char const* text = R"( contract C { - struct S { } + struct S { uint a; } modifier m(S storage x) { x; _; diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol new file mode 100644 index 000000000000..87bc2ffe8bb2 --- /dev/null +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -0,0 +1,5 @@ +contract test { + struct A {} +} +// ---- +// SyntaxError: Defining empty structs is disallowed. From ebb12756adfe95ae694ed7e141890c6d47e8b7b4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 15:00:33 +0100 Subject: [PATCH 199/242] Still allow empty structs for non-0.5.0 mode --- Changelog.md | 2 +- libsolidity/analysis/SyntaxChecker.cpp | 9 ++++++++- test/libsolidity/syntaxTests/empty_struct.sol | 2 +- test/libsolidity/syntaxTests/empty_struct_050.sol | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/empty_struct_050.sol diff --git a/Changelog.md b/Changelog.md index 3fa0c48563b6..e931483496c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. @@ -12,7 +13,6 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. - * Syntax Checker: Issue error for empty structs. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index e03417a957e0..3a32810bd2a9 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -258,7 +258,14 @@ bool SyntaxChecker::visit(VariableDeclaration const& _declaration) bool SyntaxChecker::visit(StructDefinition const& _struct) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_struct.members().empty()) - m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + { + if (v050) + m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + else + m_errorReporter.warning(_struct.location(), "Defining empty structs is deprecated."); + } return true; } diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol index 87bc2ffe8bb2..dcced618a912 100644 --- a/test/libsolidity/syntaxTests/empty_struct.sol +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -2,4 +2,4 @@ contract test { struct A {} } // ---- -// SyntaxError: Defining empty structs is disallowed. +// Warning: Defining empty structs is deprecated. diff --git a/test/libsolidity/syntaxTests/empty_struct_050.sol b/test/libsolidity/syntaxTests/empty_struct_050.sol new file mode 100644 index 000000000000..dbec93c495cc --- /dev/null +++ b/test/libsolidity/syntaxTests/empty_struct_050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; +contract test { + struct A {} +} +// ---- +// SyntaxError: Defining empty structs is disallowed. From 8a18f22b878b86fff8e2f49a99397320180d4b22 Mon Sep 17 00:00:00 2001 From: Anthony Broad-Crawford Date: Fri, 16 Mar 2018 10:52:04 -0500 Subject: [PATCH 200/242] Support for error on non-existant or irregular files with command line option to ignore --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 31 ++++++++++++++++++++++++++----- solc/CommandLineInterface.h | 2 +- test/cmdlineTests.sh | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5cd4c2f85c5a..a20a9a980b2d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.22 (unreleased) Features: + * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index d3d234c343c1..8f81e799485a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -116,6 +116,7 @@ static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; +static string const g_strIgnoreMissingFiles = "ignore-missing"; static string const g_argAbi = g_strAbi; static string const g_argPrettyJson = g_strPrettyJson; @@ -152,6 +153,7 @@ static string const g_argStandardJSON = g_strStandardJSON; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; +static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; /// Possible arguments to for --combined-json static set const g_combinedJsonArgs @@ -398,8 +400,9 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) } } -void CommandLineInterface::readInputFilesAndConfigureRemappings() +bool CommandLineInterface::readInputFilesAndConfigureRemappings() { + bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); bool addStdin = false; if (!m_args.count(g_argInputFile)) addStdin = true; @@ -416,13 +419,27 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() auto infile = boost::filesystem::path(path); if (!boost::filesystem::exists(infile)) { - cerr << "Skipping non-existent input file \"" << infile << "\"" << endl; + if (!ignoreMissing) + { + cerr << "\"" << infile << "\" is not found" << endl; + return false; + } + else + cerr << "\"" << infile << "\" is not found. Skipping." << endl; + continue; } if (!boost::filesystem::is_regular_file(infile)) { - cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl; + if (!ignoreMissing) + { + cerr << "\"" << infile << "\" is not a valid file" << endl; + return false; + } + else + cerr << "\"" << infile << "\" is not a valid file. Skipping." << endl; + continue; } @@ -433,6 +450,8 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() } if (addStdin) m_sourceCodes[g_stdinFileName] = dev::readStandardInput(); + + return true; } bool CommandLineInterface::parseLibraryOption(string const& _input) @@ -599,7 +618,8 @@ Allowed options)", g_argAllowPaths.c_str(), po::value()->value_name("path(s)"), "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." - ); + ) + (g_argIgnoreMissingFiles.c_str(), "Ignore missing files."); po::options_description outputComponents("Output Components"); outputComponents.add_options() (g_argAst.c_str(), "AST of all source files.") @@ -741,7 +761,8 @@ bool CommandLineInterface::processInput() return true; } - readInputFilesAndConfigureRemappings(); + if (!readInputFilesAndConfigureRemappings()) + return false; if (m_args.count(g_argLibraries)) for (string const& library: m_args[g_argLibraries].as>()) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 303023fcbe41..45ec1eb582b5 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -72,7 +72,7 @@ class CommandLineInterface void handleFormal(); /// Fills @a m_sourceCodes initially and @a m_redirects. - void readInputFilesAndConfigureRemappings(); + bool readInputFilesAndConfigureRemappings(); /// Tries to read from the file @a _input or interprets _input literally if that fails. /// It then tries to parse the contents and appends to m_libraries. bool parseLibraryOption(std::string const& _input); diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 92f9569a1263..1137c7b0518a 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -32,7 +32,7 @@ REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) echo $REPO_ROOT SOLC="$REPO_ROOT/build/solc/solc" -FULLARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" echo "Checking that the bug list is up to date..." "$REPO_ROOT"/scripts/update_bugs_by_version.py From 5c8a6aac698f6d084a22c6ec9f282b3f26ddb8bb Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 16:06:40 +0100 Subject: [PATCH 201/242] Prevent encoding of weird types and support packed encoding of external function types. --- Changelog.md | 2 ++ libsolidity/analysis/TypeChecker.cpp | 11 ++++++++ libsolidity/ast/Types.cpp | 2 ++ libsolidity/ast/Types.h | 11 +++++--- libsolidity/codegen/CompilerUtils.cpp | 1 - test/libsolidity/SolidityEndToEndTest.cpp | 25 +++++++++++++++++++ ...nspecified_encoding_internal_functions.sol | 11 ++++++++ ...ith_unspecified_encoding_special_types.sol | 13 ++++++++++ ...ypes_with_unspecified_encoding_structs.sol | 13 ++++++++++ .../types_with_unspecified_encoding_types.sol | 16 ++++++++++++ .../types_without_encoding_problems.sol | 9 +++++++ 11 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol diff --git a/Changelog.md b/Changelog.md index a20a9a980b2d..af09f8045d36 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,11 +10,13 @@ Bugfixes: * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. + * Code Generator: Implement packed encoding of external function types. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. + * Type System: Prevent encoding of weird types. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6e287f838eb1..620dfca48162 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1644,9 +1644,20 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto const& argType = type(*arguments[i]); if (functionType->takesArbitraryParameters()) { + bool errored = false; if (auto t = dynamic_cast(argType.get())) if (!t->mobileType()) + { m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); + errored = true; + } + if (!errored && !( + argType->mobileType() && + argType->mobileType()->interfaceType(false) && + argType->mobileType()->interfaceType(false)->encodingType() && + !(dynamic_cast(argType->mobileType()->interfaceType(false)->encodingType().get())) + )) + m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) m_errorReporter.typeError( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 720215c9008d..4c462d098881 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1262,6 +1262,8 @@ bool ContractType::isPayable() const TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const { + if (isSuper()) + return TypePointer{}; return _operator == Token::Delete ? make_shared() : TypePointer(); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c930dd240713..b7e64891897e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -692,22 +692,27 @@ class ContractType: public Type virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { + solAssert(!isSuper(), ""); return encodingType()->calldataEncodedSize(_padded); } - virtual unsigned storageBytes() const override { return 20; } - virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } + virtual bool canLiveOutsideStorage() const override { return !isSuper(); } virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } - virtual bool isValueType() const override { return true; } + virtual bool isValueType() const override { return !isSuper(); } virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { + if (isSuper()) + return TypePointer{}; return std::make_shared(160, IntegerType::Modifier::Address); } virtual TypePointer interfaceType(bool _inLibrary) const override { + if (isSuper()) + return TypePointer{}; return _inLibrary ? shared_from_this() : encodingType(); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 68f0b3a1192a..676d5d4e3546 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -142,7 +142,6 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound dynamic_cast(_type).kind() == FunctionType::Kind::External ) { - solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented."); combineExternalFunctionType(true); m_context << Instruction::DUP2 << Instruction::MSTORE; m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a866e46c35f7..f5813aed1ca6 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2077,6 +2077,31 @@ BOOST_AUTO_TEST_CASE(packed_keccak256) testContractAgainstCpp("a(bytes32)", f, u256(-1)); } +BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types) +{ + char const* sourceCode = R"( + contract test { + uint120[3] x; + function f() view returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) { + uint120[] memory y = new uint120[](3); + x[0] = y[0] = uint120(-2); + x[1] = y[1] = uint120(-3); + x[2] = y[2] = uint120(-4); + hash1 = keccak256(x); + hash2 = keccak256(y); + hash3 = keccak256(this.f); + } + } + )"; + compileAndRun(sourceCode); + // Strangely, arrays are encoded with intra-element padding. + ABI_CHECK(callContractFunction("f()"), encodeArgs( + dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + dev::keccak256(fromHex(m_contractAddress.hex() + "26121ff0")) + )); +} + BOOST_AUTO_TEST_CASE(packed_sha256) { char const* sourceCode = R"( diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol new file mode 100644 index 000000000000..9f57c3a4ac44 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + bytes32 h = keccak256(keccak256, f, this.f.gas, block.blockhash); + h; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol new file mode 100644 index 000000000000..a7d13215b171 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + bool a = address(this).call(address(this).delegatecall, super); + bool b = address(this).delegatecall(log0, tx, mulmod); + a; b; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol new file mode 100644 index 000000000000..f27290fdcd3d --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -0,0 +1,13 @@ +contract C { + struct S { uint x; } + S s; + struct T { } + T t; + function f() public pure { + bytes32 a = sha256(s, t); + a; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol new file mode 100644 index 000000000000..991b2237ac2f --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -0,0 +1,16 @@ +contract C { + struct S { uint x; } + S s; + struct T { } + T t; + enum A { X, Y } + function f() public pure { + bool a = address(this).delegatecall(S, A, A.X, T, uint, uint[]); + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol new file mode 100644 index 000000000000..c83645486406 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol @@ -0,0 +1,9 @@ +contract C { + uint[3] sarr; + function f() view public { + uint[3] memory arr; + bytes32 h = keccak256(this.f, arr, sarr); + h; + } +} +// ---- From 8aadc179ae9f47a7d56c85b2af492318dc571d5e Mon Sep 17 00:00:00 2001 From: wbt Date: Fri, 30 Mar 2018 15:28:15 -0400 Subject: [PATCH 202/242] Fix small formatting issue in which a constant was not treated as code, inconsistent with surrounding examples. --- docs/style-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 2261746f6f4e..ee1ea4bd2b38 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -904,7 +904,7 @@ Constants ========= Constants should be named with all capital letters with underscores separating -words. Examples: ``MAX_BLOCKS``, `TOKEN_NAME`, ``TOKEN_TICKER``, ``CONTRACT_VERSION``. +words. Examples: ``MAX_BLOCKS``, ``TOKEN_NAME``, ``TOKEN_TICKER``, ``CONTRACT_VERSION``. Modifier Names From 076c0754ea8e37f20ba28c25864053f129176081 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 31 Mar 2018 00:27:01 +0100 Subject: [PATCH 203/242] Update tests for empty structs --- .../specialFunctions/types_with_unspecified_encoding_structs.sol | 1 + .../specialFunctions/types_with_unspecified_encoding_types.sol | 1 + 2 files changed, 2 insertions(+) diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol index f27290fdcd3d..378155e981b1 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -9,5 +9,6 @@ contract C { } } // ---- +// Warning: Defining empty structs is deprecated. // TypeError: This type cannot be encoded. // TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol index 991b2237ac2f..6e073fd883e3 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -9,6 +9,7 @@ contract C { } } // ---- +// Warning: Defining empty structs is deprecated. // TypeError: This type cannot be encoded. // TypeError: This type cannot be encoded. // TypeError: This type cannot be encoded. From e37b619593cc926873279dc1d5f532a7a13b6fa4 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Fri, 16 Feb 2018 10:11:07 -0500 Subject: [PATCH 204/242] Set default fixed point decimal places to 18 --- docs/abi-spec.rst | 6 +++--- docs/types.rst | 2 +- libsolidity/ast/Types.cpp | 4 ++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 4d84a7daee17..98301fdc48d0 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -54,7 +54,7 @@ The following elementary types exist: - ``ufixedx``: unsigned variant of ``fixedx``. -- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively. For computing the function selector, ``fixed128x19`` and ``ufixed128x19`` have to be used. +- ``fixed``, ``ufixed``: synonyms for ``fixed128x18``, ``ufixed128x18`` respectively. For computing the function selector, ``fixed128x18`` and ``ufixed128x18`` have to be used. - ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. @@ -164,9 +164,9 @@ on the type of ``X`` being - ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. -- ``fixed``: as in the ``fixed128x19`` case +- ``fixed``: as in the ``fixed128x18`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. -- ``ufixed``: as in the ``ufixed128x19`` case +- ``ufixed``: as in the ``ufixed128x18`` case - ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. diff --git a/docs/types.rst b/docs/types.rst index e704687ee949..5de6d07e9f36 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -81,7 +81,7 @@ Fixed Point Numbers ``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. +``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively. Operators: diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4c462d098881..42fd1c3da7e7 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -208,9 +208,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::UInt: return make_shared(256, IntegerType::Modifier::Unsigned); case Token::Fixed: - return make_shared(128, 19, FixedPointType::Modifier::Signed); + return make_shared(128, 18, FixedPointType::Modifier::Signed); case Token::UFixed: - return make_shared(128, 19, FixedPointType::Modifier::Unsigned); + return make_shared(128, 18, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared(1); case Token::Address: diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dd29bf48ec1d..f18c906d4cee 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2452,8 +2452,8 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared(31)); BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared(32)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared(128, 19, FixedPointType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared(128, 19, FixedPointType::Modifier::Unsigned)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared(128, 18, FixedPointType::Modifier::Signed)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared(128, 18, FixedPointType::Modifier::Unsigned)); } BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) @@ -4471,7 +4471,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) } } )"; - CHECK_ERROR(text, TypeError, "Type fixed128x19 is not implicitly convertible to expected type int256"); + CHECK_ERROR(text, TypeError, "Type fixed128x18 is not implicitly convertible to expected type int256"); } BOOST_AUTO_TEST_CASE(rational_unary_operation) @@ -4589,7 +4589,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) } } )"; - CHECK_ERROR(text, TypeError, "Type ufixed128x19 is not implicitly convertible to expected type ufixed248x8"); + CHECK_ERROR(text, TypeError, "Type ufixed128x18 is not implicitly convertible to expected type ufixed248x8"); } BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) @@ -4676,7 +4676,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) } } )"; - CHECK_ERROR(text, TypeError, "fixed128x19 is not implicitly convertible to expected type bytes32"); + CHECK_ERROR(text, TypeError, "fixed128x18 is not implicitly convertible to expected type bytes32"); } BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) From 29df18d4d79f35233dfc5dabb8ab95dd17580417 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 31 Mar 2018 01:11:56 +0100 Subject: [PATCH 205/242] Remove brew linkapps from the installation instructions --- docs/installing-solidity.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index e26870f03912..6726ded90760 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -122,7 +122,6 @@ We will re-add the pre-built bottles soon. brew upgrade brew tap ethereum/ethereum brew install solidity - brew linkapps solidity If you need a specific version of Solidity you can install a Homebrew formula directly from Github. From 826de65e2d9db8f05879a6f4557a9c50936610d1 Mon Sep 17 00:00:00 2001 From: Haoliang Yu Date: Mon, 2 Apr 2018 22:22:38 -0400 Subject: [PATCH 206/242] fix a wrong number --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 121c4de0edd9..8cc4f6b2b3ac 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -679,7 +679,7 @@ candidate, resolution fails. } } -Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8`` +Calling ``f(50)`` would create a type error since ``50`` 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``. From 8fe1cfb12ef49a74eaebed56d160e88cfd9a4de2 Mon Sep 17 00:00:00 2001 From: bitshift Date: Fri, 9 Mar 2018 17:38:17 +0100 Subject: [PATCH 207/242] Defaults to external visibility for interfaces. --- libsolidity/analysis/StaticAnalyzer.cpp | 8 ++++++++ libsolidity/analysis/SyntaxChecker.cpp | 15 +++------------ .../visibility/interface/function_default.sol | 6 ++++++ .../visibility/interface/function_default050.sol | 7 +++++++ 4 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_default.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_default050.sol diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 6aee260e73c0..20464765b774 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -50,6 +50,14 @@ void StaticAnalyzer::endVisit(ContractDefinition const&) bool StaticAnalyzer::visit(FunctionDefinition const& _function) { + const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface; + if (_function.noVisibilitySpecified()) + m_errorReporter.warning( + _function.location(), + "No visibility specified. Defaulting to \"" + + (isInterface ? "external" : Declaration::visibilityToString(_function.visibility())) + + "\"." + ); if (_function.isImplemented()) m_currentFunction = &_function; else diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index ddac194b9b1d..5de9a2705c85 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -214,18 +214,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) { bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (_function.noVisibilitySpecified()) - { - if (v050) - m_errorReporter.syntaxError(_function.location(), "No visibility specified."); - else - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\"." - ); - } + if (v050 && _function.noVisibilitySpecified()) + m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + return true; } diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol new file mode 100644 index 000000000000..9889d038a8f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol @@ -0,0 +1,6 @@ +interface I { + function f(); +} +// ---- +// Warning: Functions in interfaces should be declared external. +// Warning: No visibility specified. Defaulting to "external". diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol new file mode 100644 index 000000000000..127d4e92a7ab --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +interface I { + function f(); +} +// ---- +// SyntaxError: No visibility specified. +// TypeError: Functions in interfaces must be declared external. From 601659c3844767686a324ea363498e85320c92b1 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Fri, 23 Mar 2018 15:09:45 +0100 Subject: [PATCH 208/242] Moves all interface function visibility related tests. --- .../SolidityNameAndTypeResolution.cpp | 48 ------------------- .../interface/function_external050.sol | 5 ++ .../interface/function_internal.sol | 5 ++ .../visibility/interface/function_private.sol | 5 ++ .../visibility/interface/function_public.sol | 5 ++ .../interface/function_public050.sol | 6 +++ 6 files changed, 26 insertions(+), 48 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_external050.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_internal.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_private.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_public.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_public050.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5269bc5f572..1e98d50d1153 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6429,54 +6429,6 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies) CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation"); } -BOOST_AUTO_TEST_CASE(interface_function_external) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - interface I { - function f() external; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_function_public) -{ - char const* text = R"( - interface I { - function f() public; - } - )"; - CHECK_WARNING(text, "Functions in interfaces should be declared external."); - text = R"( - pragma experimental "v0.5.0"; - interface I { - function f() public; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces must be declared external."); -} - -BOOST_AUTO_TEST_CASE(interface_function_internal) -{ - char const* text = R"( - interface I { - function f() internal; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); -} - -BOOST_AUTO_TEST_CASE(interface_function_private) -{ - char const* text = R"( - interface I { - function f() private; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); -} - BOOST_AUTO_TEST_CASE(interface_events) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol new file mode 100644 index 000000000000..3f0a9acabc04 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol @@ -0,0 +1,5 @@ +pragma experimental "v0.5.0"; +interface I { + function f() external; +} +// ---- diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol new file mode 100644 index 000000000000..a1cf5246361f --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol @@ -0,0 +1,5 @@ +interface I { + function f() internal; +} +// ---- +// TypeError: Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol new file mode 100644 index 000000000000..887ebd4bfef2 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol @@ -0,0 +1,5 @@ +interface I { + function f() private; +} +// ---- +// TypeError: Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol new file mode 100644 index 000000000000..146d4f5b5fa8 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol @@ -0,0 +1,5 @@ +interface I { + function f() public; +} +// ---- +// Warning: Functions in interfaces should be declared external. \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol new file mode 100644 index 000000000000..f957f0b4c564 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; +interface I { + function f() public; +} +// ---- +// TypeError: Functions in interfaces must be declared external. \ No newline at end of file From f9efa417492916546d23115da7a55e86090d47dd Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 28 Mar 2018 18:10:32 +0200 Subject: [PATCH 209/242] Makes visibility warning more concise. --- libsolidity/analysis/StaticAnalyzer.cpp | 6 ++++-- .../syntaxTests/visibility/interface/function_default.sol | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 20464765b774..d96f87484b94 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -51,12 +51,14 @@ void StaticAnalyzer::endVisit(ContractDefinition const&) bool StaticAnalyzer::visit(FunctionDefinition const& _function) { const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface; + if (_function.noVisibilitySpecified()) m_errorReporter.warning( _function.location(), "No visibility specified. Defaulting to \"" + - (isInterface ? "external" : Declaration::visibilityToString(_function.visibility())) + - "\"." + Declaration::visibilityToString(_function.visibility()) + + "\". " + + (isInterface ? "In interfaces it defaults to external." : "") ); if (_function.isImplemented()) m_currentFunction = &_function; diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol index 9889d038a8f2..7b9044dd2f91 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol @@ -3,4 +3,4 @@ interface I { } // ---- // Warning: Functions in interfaces should be declared external. -// Warning: No visibility specified. Defaulting to "external". +// Warning: No visibility specified. Defaulting to "public". In interfaces it defaults to external. From 6777f7a57fed6b39128773f13084da729dd64588 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 19:41:29 +0100 Subject: [PATCH 210/242] Optimize across MLOAD if MSIZE is not used. --- libevmasm/Assembly.cpp | 4 ++- libevmasm/CommonSubexpressionEliminator.h | 8 +++-- libevmasm/Instruction.cpp | 2 +- libevmasm/SemanticInformation.cpp | 7 ++++- libevmasm/SemanticInformation.h | 3 +- libsolidity/ast/Types.h | 13 ++++++++ libsolidity/codegen/CompilerUtils.cpp | 36 +++++++++++++++++----- libsolidity/codegen/ExpressionCompiler.cpp | 21 +++---------- test/libevmasm/Optimiser.cpp | 5 +-- test/libsolidity/SolidityEndToEndTest.cpp | 23 +++++++++++++- 10 files changed, 87 insertions(+), 35 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index bd4ebf59a506..b71bc80cb660 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -438,13 +438,15 @@ map Assembly::optimiseInternal( // function types that can be stored in storage. AssemblyItems optimisedItems; + bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end()); + auto iter = m_items.begin(); while (iter != m_items.end()) { KnownState emptyState; CommonSubexpressionEliminator eliminator(emptyState); auto orig = iter; - iter = eliminator.feedItems(iter, m_items.end()); + iter = eliminator.feedItems(iter, m_items.end(), usesMSize); bool shouldReplace = false; AssemblyItems optimisedChunk; try diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 0b957a0ec3bf..b20de24697f3 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -65,8 +65,9 @@ class CommonSubexpressionEliminator /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. + /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect template - _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end); + _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end, bool _msizeImportant); /// @returns the resulting items after optimization. AssemblyItems getOptimizedItems(); @@ -168,11 +169,12 @@ class CSECodeGenerator template _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _iterator, - _AssemblyItemIterator _end + _AssemblyItemIterator _end, + bool _msizeImportant ) { assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator."); - for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator) + for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant); ++_iterator) feedItem(*_iterator); if (_iterator != _end) m_breakingItem = &(*_iterator++); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index a677a631cf61..f9bbad2cf321 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -199,7 +199,7 @@ static const std::map c_instructionInfo = { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, - { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, false, Tier::Special } }, + { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } }, { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } }, { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } }, { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 03870f7cab51..71267ee89c7a 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) +bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant) { switch (_item.type()) { @@ -59,6 +59,11 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) return false; if (_item.instruction() == Instruction::MSTORE) return false; + if (!_msizeImportant && ( + _item.instruction() == Instruction::MLOAD || + _item.instruction() == Instruction::KECCAK256 + )) + return false; //@todo: We do not handle the following memory instructions for now: // calldatacopy, codecopy, extcodecopy, mstore8, // msize (note that msize also depends on memory read access) diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 83656252c637..8bdc70becc88 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -38,7 +38,8 @@ class AssemblyItem; struct SemanticInformation { /// @returns true if the given items starts a new block for common subexpression analysis. - static bool breaksCSEAnalysisBlock(AssemblyItem const& _item); + /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. + static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); /// @returns true if the item is a two-argument operation whose value does not depend on the /// order of its arguments. static bool isCommutativeOperation(AssemblyItem const& _item); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b7e64891897e..2c392705d559 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -229,6 +229,9 @@ class Type: private boost::noncopyable, public std::enable_shared_from_thistoString(_short) + ")"; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; @@ -1154,6 +1163,7 @@ class ModifierType: public Type virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1179,6 +1189,7 @@ class ModuleType: public Type virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1209,6 +1220,7 @@ class MagicType: public Type virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1238,6 +1250,7 @@ class InaccessibleDynamicType: public Type virtual bool canLiveOutsideStorage() const override { return false; } virtual bool isValueType() const override { return true; } virtual unsigned sizeOnStack() const override { return 1; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } virtual TypePointer decodingType() const override { return std::make_shared(256); } }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 676d5d4e3546..deaef017adf6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -495,14 +495,34 @@ void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromM void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) { - auto repeat = m_context.newTag(); - m_context << repeat; - pushZeroValue(*_type.baseType()); - storeInMemoryDynamic(*_type.baseType()); - m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1; - m_context << Instruction::SUB << Instruction::SWAP1; - m_context << Instruction::DUP2; - m_context.appendConditionalJumpTo(repeat); + if (_type.baseType()->hasSimpleZeroValueInMemory()) + { + solAssert(_type.baseType()->isValueType(), ""); + Whiskers templ(R"({ + let size := mul(length, ) + // cheap way of zero-initializing a memory range + codecopy(memptr, codesize(), size) + memptr := add(memptr, size) + })"); + templ("element_size", to_string(_type.baseType()->memoryHeadSize())); + m_context.appendInlineAssembly(templ.render(), {"length", "memptr"}); + } + else + { + // TODO: Potential optimization: + // When we create a new multi-dimensional dynamic array, each element + // is initialized to an empty array. It actually does not hurt + // to re-use exactly the same empty array for all elements. Currently, + // a new one is created each time. + auto repeat = m_context.newTag(); + m_context << repeat; + pushZeroValue(*_type.baseType()); + storeInMemoryDynamic(*_type.baseType()); + m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1; + m_context << Instruction::SUB << Instruction::SWAP1; + m_context << Instruction::DUP2; + m_context.appendConditionalJumpTo(repeat); + } m_context << Instruction::SWAP1 << Instruction::POP; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 9e2d30d5e399..76aa68438bc3 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -850,8 +850,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::ObjectCreation: { - // Will allocate at the end of memory (MSIZE) and not write at all unless the base - // type is dynamically sized. ArrayType const& arrayType = dynamic_cast(*_functionCall.annotation().type); _functionCall.expression().accept(*this); solAssert(arguments.size() == 1, ""); @@ -861,15 +859,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().convertType(*arguments[0]->annotation().type, IntegerType(256)); // Stack: requested_length - // Allocate at max(MSIZE, freeMemoryPointer) utils().fetchFreeMemoryPointer(); - m_context << Instruction::DUP1 << Instruction::MSIZE; - m_context << Instruction::LT; - auto initialise = m_context.appendConditionalJump(); - // Free memory pointer does not point to empty memory, use MSIZE. - m_context << Instruction::POP; - m_context << Instruction::MSIZE; - m_context << initialise; // Stack: requested_length memptr m_context << Instruction::SWAP1; @@ -894,13 +884,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Check if length is zero m_context << Instruction::DUP1 << Instruction::ISZERO; auto skipInit = m_context.appendConditionalJump(); - - // We only have to initialise if the base type is a not a value type. - if (dynamic_cast(arrayType.baseType().get())) - { - m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; - utils().zeroInitialiseMemoryArray(arrayType); - } + // Always initialize because the free memory pointer might point at + // a dirty memory area. + m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; + utils().zeroInitialiseMemoryArray(arrayType); m_context << skipInit; m_context << Instruction::POP; break; diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index f630b30486e3..28f6b8c0f6d5 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -69,8 +69,9 @@ namespace { AssemblyItems input = addDummyLocations(_input); + bool usesMsize = (find(_input.begin(), _input.end(), AssemblyItem{Instruction::MSIZE}) != _input.end()); eth::CommonSubexpressionEliminator cse(_state); - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end()); AssemblyItems output = cse.getOptimizedItems(); for (AssemblyItem const& item: output) @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap) Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1, Instruction::DIV, u256(0xff), Instruction::AND }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.end()); AssemblyItems output = cse.getOptimizedItems(); BOOST_CHECK(!output.empty()); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f5813aed1ca6..7fd65c1417bf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9119,7 +9119,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) int mutex; function t() returns (uint) { if (mutex > 0) - return 7; + { assembly { mstore(0, 7) return(0, 0x20) } } mutex = 1; // Avoid re-executing this function if we jump somewhere. x(); @@ -9132,6 +9132,27 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) ABI_CHECK(callContractFunction("t()"), encodeArgs()); } +BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array) +{ + char const* sourceCode = R"( + contract C { + int mutex; + function t() returns (uint) { + if (mutex > 0) + { assembly { mstore(0, 7) return(0, 0x20) } } + mutex = 1; + // Avoid re-executing this function if we jump somewhere. + function() internal returns (uint)[200] x; + x[0](); + return 2; + } + } + )"; + + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("t()"), encodeArgs()); +} + BOOST_AUTO_TEST_CASE(pass_function_types_internally) { char const* sourceCode = R"( From 138dba1a3fbb475a21a546bc39a55a746647439b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 13:00:16 +0100 Subject: [PATCH 211/242] Test number of sstore operations. --- test/libsolidity/SolidityOptimizer.cpp | 40 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 33039ca94fce..cf4550c718dd 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -74,11 +74,11 @@ class OptimizerTestFramework: public SolidityExecutionFramework unsigned const _optimizeRuns = 200 ) { - bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns); + m_nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns); m_nonOptimizedContract = m_contractAddress; - bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns); - size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode); - size_t optimizedSize = numInstructions(optimizedBytecode); + m_optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns); + size_t nonOptimizedSize = numInstructions(m_nonOptimizedBytecode); + size_t optimizedSize = numInstructions(m_optimizedBytecode); BOOST_CHECK_MESSAGE( _optimizeRuns < 50 || optimizedSize < nonOptimizedSize, string("Optimizer did not reduce bytecode size. Non-optimized size: ") + @@ -104,7 +104,7 @@ class OptimizerTestFramework: public SolidityExecutionFramework /// @returns the number of intructions in the given bytecode, not taking the metadata hash /// into account. - size_t numInstructions(bytes const& _bytecode) + size_t numInstructions(bytes const& _bytecode, boost::optional _which = boost::optional{}) { BOOST_REQUIRE(_bytecode.size() > 5); size_t metadataSize = (_bytecode[_bytecode.size() - 2] << 8) + _bytecode[_bytecode.size() - 1]; @@ -112,13 +112,16 @@ class OptimizerTestFramework: public SolidityExecutionFramework BOOST_REQUIRE(_bytecode.size() >= metadataSize + 2); bytes realCode = bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2); size_t instructions = 0; - solidity::eachInstruction(realCode, [&](Instruction, u256 const&) { - instructions++; + solidity::eachInstruction(realCode, [&](Instruction _instr, u256 const&) { + if (!_which || *_which == _instr) + instructions++; }); return instructions; } protected: + bytes m_nonOptimizedBytecode; + bytes m_optimizedBytecode; Address m_optimizedContract; Address m_nonOptimizedContract; }; @@ -581,6 +584,29 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) compareVersions("test()"); } +BOOST_AUTO_TEST_CASE(optimise_multi_stores) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint16 a; uint16 b; uint16[3] c; uint[] dyn; } + uint padding; + S[] s; + function f() public returns (uint16, uint16, uint16[3], uint) { + uint16[3] memory c; + c[0] = 7; + c[1] = 8; + c[2] = 9; + s.push(S(1, 2, c, new uint[](4))); + return (s[0].a, s[0].b, s[0].c, s[0].dyn[2]); + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 13); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 11); +} + BOOST_AUTO_TEST_SUITE_END() } From e64e397f2417fc9a678690614a775fa88514f1dc Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Mar 2018 12:11:07 +0200 Subject: [PATCH 212/242] Add memory array init test. --- test/libsolidity/SolidityEndToEndTest.cpp | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7fd65c1417bf..38d3ce4dfe64 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8756,6 +8756,32 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); } +BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) +{ + // Memory arrays are initialized using codecopy past the size of the code. + // This test checks that it also works in the constructor context. + char const* sourceCode = R"( + contract C { + bool public success; + function C() public { + // Make memory dirty. + assembly { + for { let i := 0 } lt(i, 64) { i := add(i, 1) } { + mstore(msize, not(0)) + } + } + uint16[3] memory c; + require(c[0] == 0 && c[1] == 0 && c[2] == 0); + uint16[] memory x = new uint16[](3); + require(x[0] == 0 && x[1] == 0 && x[2] == 0); + success = true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) { char const* sourceCode = R"( From deadff263fbf4d5da911d7c544821cc77081a6d3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Mar 2018 12:12:12 +0200 Subject: [PATCH 213/242] Changelog entry. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index d35a2b45603f..88e88545d94b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,11 @@ ### 0.4.22 (unreleased) Features: + * Code Generator: Initialize arrays without using ``msize()``. * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). Bugfixes: From a54fdc495f54bd16c329ca3f79d533d540cb9a3e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 19:02:15 +0100 Subject: [PATCH 214/242] Fix: Treat empty base constructor argument list as not provided. --- Changelog.md | 1 + libsolidity/codegen/ContractCompiler.cpp | 8 +++++--- .../inheritance/base_arguments_empty_parentheses.sol | 6 ++++++ .../syntaxTests/inheritance/too_few_base_arguments.sol | 10 ++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol diff --git a/Changelog.md b/Changelog.md index d35a2b45603f..70e987515e95 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Code Generator: Implement packed encoding of external function types. + * Code Generator: Treat empty base constructor argument list as not provided. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 791edc65a9d5..ebd9139a744c 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -143,8 +143,9 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c for (auto const& modifier: constructor->modifiers()) { auto baseContract = dynamic_cast( - modifier->name()->annotation().referencedDeclaration); - if (baseContract) + modifier->name()->annotation().referencedDeclaration + ); + if (baseContract && !modifier->arguments().empty()) if (m_baseArguments.count(baseContract->constructor()) == 0) m_baseArguments[baseContract->constructor()] = &modifier->arguments(); } @@ -156,7 +157,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c ); solAssert(baseContract, ""); - if (m_baseArguments.count(baseContract->constructor()) == 0) + if (!m_baseArguments.count(baseContract->constructor()) && !base->arguments().empty()) m_baseArguments[baseContract->constructor()] = &base->arguments(); } } @@ -238,6 +239,7 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc solAssert(m_baseArguments.count(&_constructor), ""); std::vector> const* arguments = m_baseArguments[&_constructor]; solAssert(arguments, ""); + solAssert(arguments->size() == constructorType.parameterTypes().size(), ""); for (unsigned i = 0; i < arguments->size(); ++i) compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]); } diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol new file mode 100644 index 000000000000..76df0657fae8 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -0,0 +1,6 @@ +contract Base { + function Base(uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base(), Derived() { } +contract Derived3 is Base, Derived {} diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol new file mode 100644 index 000000000000..82aba308840b --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -0,0 +1,10 @@ +contract Base { + function Base(uint, uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base { + function Derived2() Base(2) public { } +} +// ---- +// TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2. +// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 2. From c42caedec2b6dc2bfb268f538eecb1d4ee18f9d8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 18:19:45 +0100 Subject: [PATCH 215/242] Extract recursive struct tests. --- .../SolidityNameAndTypeResolution.cpp | 93 ------------------- .../recursion/return_recursive_structs.sol | 7 ++ .../recursion/return_recursive_structs2.sol | 7 ++ .../recursion/return_recursive_structs3.sol | 8 ++ .../struct_definition_directly_recursive.sol | 8 ++ ...struct_definition_indirectly_recursive.sol | 12 +++ ...struct_definition_not_really_recursive.sol | 4 + ...truct_definition_recursion_via_mapping.sol | 7 ++ 8 files changed, 53 insertions(+), 93 deletions(-) create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcdc15199c01..2a03392e4966 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -92,61 +92,6 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive) -{ - char const* text = R"( - contract test { - struct MyStructName { - address addr; - MyStructName x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Recursive struct definition."); -} - -BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) -{ - char const* text = R"( - contract test { - struct MyStructName1 { - address addr; - uint256 count; - MyStructName2 x; - } - struct MyStructName2 { - MyStructName1 x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Recursive struct definition."); -} - -BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) -{ - char const* text = R"( - contract test { - struct s1 { uint a; } - struct s2 { s1 x; s1 y; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping) -{ - char const* text = R"( - contract test { - struct MyStructName1 { - address addr; - uint256 count; - mapping(uint => MyStructName1) x; - } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(type_inference_smoke_test) { char const* text = R"( @@ -6193,44 +6138,6 @@ BOOST_AUTO_TEST_CASE(read_returned_struct) )"; CHECK_WARNING(text, "Experimental features"); } - -BOOST_AUTO_TEST_CASE(return_recursive_structs) -{ - char const* text = R"( - contract C { - struct S { uint a; S[] sub; } - function f() returns (uint, S) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(return_recursive_structs2) -{ - char const* text = R"( - contract C { - struct S { uint a; S[2][] sub; } - function f() returns (uint, S) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(return_recursive_structs3) -{ - char const* text = R"( - contract C { - struct S { uint a; S[][][] sub; } - struct T { S s; } - function f() returns (uint x, T t) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol new file mode 100644 index 000000000000..c02a8aa48b11 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol @@ -0,0 +1,7 @@ +contract C { + struct S { uint a; S[] sub; } + function f() public pure returns (uint, S) { + } +} +// ---- +// TypeError: Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol new file mode 100644 index 000000000000..e9488cf4962c --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol @@ -0,0 +1,7 @@ +contract C { + struct S { uint a; S[2][] sub; } + function f() public pure returns (uint, S) { + } +} +// ---- +// TypeError: Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol new file mode 100644 index 000000000000..6728baec6594 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol @@ -0,0 +1,8 @@ +contract C { + struct S { uint a; S[][][] sub; } + struct T { S s; } + function f() public pure returns (uint x, T t) { + } +} +// ---- +// TypeError: Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol new file mode 100644 index 000000000000..cac2e23fd9f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol @@ -0,0 +1,8 @@ +contract Test { + struct MyStructName { + address addr; + MyStructName x; + } +} +// ---- +// TypeError: Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol new file mode 100644 index 000000000000..11fc63078486 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2 x; + } + struct MyStructName2 { + MyStructName1 x; + } +} +// ---- +// TypeError: Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol new file mode 100644 index 000000000000..6ec4ee01b8f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol @@ -0,0 +1,4 @@ +contract Test { + struct S1 { uint a; } + struct S2 { S1 x; S1 y; } +} diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol new file mode 100644 index 000000000000..926981b3b174 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol @@ -0,0 +1,7 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + mapping(uint => MyStructName1) x; + } +} From 5bdadff0d8a9c32745dd46aa639283718613efdc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:07:25 +0100 Subject: [PATCH 216/242] Fix detection of recursive structs. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 5 +++++ .../recursion/multi_struct_composition.sol | 15 +++++++++++++++ .../structs/recursion/parallel_structs.sol | 15 +++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol diff --git a/Changelog.md b/Changelog.md index 70e987515e95..63c991444519 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type Checker: Fix detection of recursive structs. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 42fd1c3da7e7..42f6b5ca5250 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1972,9 +1972,12 @@ bool StructType::recursive() const if (!m_recursive.is_initialized()) { set structsSeen; + set structsProcessed; function check = [&](StructType const* t) -> bool { StructDefinition const* str = &t->structDefinition(); + if (structsProcessed.count(str)) + return false; if (structsSeen.count(str)) return true; structsSeen.insert(str); @@ -1987,6 +1990,8 @@ bool StructType::recursive() const if (check(innerStruct)) return true; } + structsSeen.erase(str); + structsProcessed.insert(str); return false; }; m_recursive = check(this); diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol new file mode 100644 index 000000000000..9a1c22f18980 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct T { U u; V v; } + + struct U { W w; } + + struct V { W w; } + + struct W { uint x; } + + function f(T) public pure { } +} +// ---- +// Warning: Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol new file mode 100644 index 000000000000..d4ad088dcb84 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + +contract TestContract +{ + struct SubStruct { + uint256 id; + } + struct TestStruct { + SubStruct subStruct1; + SubStruct subStruct2; + } + function addTestStruct(TestStruct) public pure {} +} +// ---- +// Warning: Experimental features are turned on. Do not use experimental features on live deployments. From eb5b18e8145d7d3971c450ba220f322e5f66d45c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:53:29 +0100 Subject: [PATCH 217/242] Generalize cycle detection. --- libdevcore/Algorithms.h | 76 ++++++++++++++++++++++++ libsolidity/analysis/PostTypeChecker.cpp | 36 ++++++----- libsolidity/analysis/PostTypeChecker.h | 5 +- libsolidity/ast/Types.cpp | 22 ++----- 4 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 libdevcore/Algorithms.h diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h new file mode 100644 index 000000000000..b25406684319 --- /dev/null +++ b/libdevcore/Algorithms.h @@ -0,0 +1,76 @@ +/* + 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 . +*/ +#pragma once + + +#include +#include + +namespace dev +{ + +/** + * Detector for cycles in directed graphs. It returns the first + * vertex on the path towards a cycle or a nullptr if there is + * no reachable cycle starting from a given vertex. + */ +template +class CycleDetector +{ +public: + /// Initializes the cycle detector + /// @param _visit function that is given the current vertex + /// and is supposed to call @a run on all + /// adjacent vertices. + explicit CycleDetector(std::function _visit): + m_visit(std::move(_visit)) + { } + + /// Recursively perform cycle detection starting + /// (or continuing) with @param _vertex + /// @returns the first vertex on the path towards a cycle from @a _vertex + /// or nullptr if no cycle is reachable from @a _vertex. + V const* run(V const& _vertex) + { + if (m_firstCycleVertex) + return m_firstCycleVertex; + if (m_processed.count(&_vertex)) + return nullptr; + else if (m_processing.count(&_vertex)) + return m_firstCycleVertex = &_vertex; + m_processing.insert(&_vertex); + + m_depth++; + m_visit(_vertex, *this); + m_depth--; + if (m_firstCycleVertex && m_depth == 1) + m_firstCycleVertex = &_vertex; + + m_processing.erase(&_vertex); + m_processed.insert(&_vertex); + return m_firstCycleVertex; + } + +private: + std::function m_visit; + std::set m_processing; + std::set m_processed; + size_t m_depth = 0; + V const* m_firstCycleVertex = nullptr; +}; + +} diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index fbc72e5270e9..19d0b708db4e 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include #include @@ -47,7 +49,7 @@ void PostTypeChecker::endVisit(ContractDefinition const&) { solAssert(!m_currentConstVariable, ""); for (auto declaration: m_constVariables) - if (auto identifier = findCycle(declaration)) + if (auto identifier = findCycle(*declaration)) m_errorReporter.typeError( declaration->location(), "The value of the constant " + declaration->name() + @@ -87,20 +89,24 @@ bool PostTypeChecker::visit(Identifier const& _identifier) return true; } -VariableDeclaration const* PostTypeChecker::findCycle( - VariableDeclaration const* _startingFrom, - set const& _seen -) +VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom) { - if (_seen.count(_startingFrom)) - return _startingFrom; - else if (m_constVariableDependencies.count(_startingFrom)) + auto visitor = [&](VariableDeclaration const& _variable, CycleDetector& _cycleDetector) { - set seen(_seen); - seen.insert(_startingFrom); - for (auto v: m_constVariableDependencies[_startingFrom]) - if (findCycle(v, seen)) - return v; - } - return nullptr; + // Iterating through the dependencies needs to be deterministic and thus cannot + // depend on the memory layout. + // Because of that, we sort by AST node id. + vector dependencies( + m_constVariableDependencies[&_variable].begin(), + m_constVariableDependencies[&_variable].end() + ); + sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool + { + return _a->id() < _b->id(); + }); + for (auto v: dependencies) + if (_cycleDetector.run(*v)) + return; + }; + return CycleDetector(visitor).run(_startingFrom); } diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index bafc1ae6561e..4f9dac6e7351 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -55,10 +55,7 @@ class PostTypeChecker: private ASTConstVisitor virtual bool visit(Identifier const& _identifier) override; - VariableDeclaration const* findCycle( - VariableDeclaration const* _startingFrom, - std::set const& _seen = std::set{} - ); + VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom); ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 42f6b5ca5250..ac1d3b01bb99 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1971,30 +1972,19 @@ bool StructType::recursive() const { if (!m_recursive.is_initialized()) { - set structsSeen; - set structsProcessed; - function check = [&](StructType const* t) -> bool + auto visitor = [&](StructDefinition const& _struct, CycleDetector& _cycleDetector) { - StructDefinition const* str = &t->structDefinition(); - if (structsProcessed.count(str)) - return false; - if (structsSeen.count(str)) - return true; - structsSeen.insert(str); - for (ASTPointer const& variable: str->members()) + for (ASTPointer const& variable: _struct.members()) { Type const* memberType = variable->annotation().type.get(); while (dynamic_cast(memberType)) memberType = dynamic_cast(memberType)->baseType().get(); if (StructType const* innerStruct = dynamic_cast(memberType)) - if (check(innerStruct)) - return true; + if (_cycleDetector.run(innerStruct->structDefinition())) + return; } - structsSeen.erase(str); - structsProcessed.insert(str); - return false; }; - m_recursive = check(this); + m_recursive = (CycleDetector(visitor).run(structDefinition()) != nullptr); } return *m_recursive; } From 8fdbd19a05c976908172d0f776a0f96837449683 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:54:05 +0100 Subject: [PATCH 218/242] Extract constant cycle tests. --- .../SolidityNameAndTypeResolution.cpp | 32 ------------------- .../constants/cyclic_dependency_1.sol | 5 +++ .../constants/cyclic_dependency_2.sol | 10 ++++++ .../constants/cyclic_dependency_3.sol | 11 +++++++ .../constants/cyclic_dependency_4.sol | 6 ++++ 5 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2a03392e4966..d10cbfbf6aeb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6260,38 +6260,6 @@ BOOST_AUTO_TEST_CASE(address_methods) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) -{ - char const* text = R"( - contract C { - uint constant a = a; - } - )"; - CHECK_ERROR(text, TypeError, "cyclic dependency via a"); - text = R"( - contract C { - uint constant a = b * c; - uint constant b = 7; - uint constant c = b + uint(keccak256(d)); - uint constant d = 2 + a; - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector{ - "a has a cyclic dependency via c", - "c has a cyclic dependency via d", - "d has a cyclic dependency via a" - })); - text = R"( - contract C { - uint constant a = b * c; - uint constant b = 7; - uint constant c = 4 + uint(keccak256(d)); - uint constant d = 2 + b; - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(interface) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol new file mode 100644 index 000000000000..2b6aa08835be --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = a; +} +// ---- +// TypeError: The value of the constant a has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol new file mode 100644 index 000000000000..461979f85a6a --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol @@ -0,0 +1,10 @@ +contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = b + uint(keccak256(d)); + uint constant d = 2 + a; +} +// ---- +// TypeError: The value of the constant a has a cyclic dependency via c. +// TypeError: The value of the constant c has a cyclic dependency via d. +// TypeError: The value of the constant d has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol new file mode 100644 index 000000000000..f63be05e3e59 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol @@ -0,0 +1,11 @@ +contract C { + uint constant x = a; + uint constant a = b * c; + uint constant b = c; + uint constant c = b; +} +// ---- +// TypeError: The value of the constant x has a cyclic dependency via a. +// TypeError: The value of the constant a has a cyclic dependency via b. +// TypeError: The value of the constant b has a cyclic dependency via c. +// TypeError: The value of the constant c has a cyclic dependency via b. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol new file mode 100644 index 000000000000..f01cb98eef45 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol @@ -0,0 +1,6 @@ +contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = 4 + uint(keccak256(d)); + uint constant d = 2 + b; +} \ No newline at end of file From 8cbb1d34a58ea3c3eb96758e776c780fb76c23c4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 3 Apr 2018 17:34:38 +0200 Subject: [PATCH 219/242] Build on tag in Circle. --- circle.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 2b34b1aa95ea..96e62f7b3b49 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,10 @@ +defaults: + # The default for tags is to not run, so we have to explicitly match a filter. + - build_on_tags: &build_on_tags + filters: + tags: + only: /.*/ + version: 2 jobs: build_emscripten: @@ -157,15 +164,18 @@ workflows: version: 2 build_all: jobs: - - build_emscripten + - build_emscripten: *build_on_tags - test_emscripten_solcjs: + <<: *build_on_tags requires: - build_emscripten - test_emscripten_external: + <<: *build_on_tags requires: - build_emscripten - - build_x86 + - build_x86: *build_on_tags - test_x86: + <<: *build_on_tags requires: - build_x86 - - docs + - docs: *build_on_tags From d664a599e68291166c47fcece464cb8d0af31df8 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 1 Mar 2018 18:39:01 +0100 Subject: [PATCH 220/242] Constructors are defined using the ``constructor`` keyword. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 17 +++++++- libsolidity/ast/AST.h | 3 +- libsolidity/parsing/Parser.cpp | 26 ++++++++++--- libsolidity/parsing/Parser.h | 1 + .../SolidityNameAndTypeResolution.cpp | 39 ++++++++++++++++++- .../base_arguments_empty_parentheses.sol | 2 +- .../inheritance/too_few_base_arguments.sol | 4 +- 8 files changed, 80 insertions(+), 13 deletions(-) diff --git a/Changelog.md b/Changelog.md index 34c3b0e98d0a..b9619c2796c4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). + * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b595c4d1cfa1..343b4ba81a7a 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -216,7 +216,22 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) if (v050 && _function.noVisibilitySpecified()) m_errorReporter.syntaxError(_function.location(), "No visibility specified."); - + + if (_function.isOldStyleConstructor()) + { + if (v050) + m_errorReporter.syntaxError( + _function.location(), + "Functions are not allowed to have the same name as the contract. " + "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." + ); + else + m_errorReporter.warning( + _function.location(), + "Defining constructors as functions with the same name as the contract is deprecated. " + "Use \"constructor(...) { ... }\" instead." + ); + } return true; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9c67d3549983..56bb412c9d49 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -607,7 +607,8 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } - bool isFallback() const { return name().empty(); } + bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); } + bool isFallback() const { return !m_isConstructor && name().empty(); } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector> const& modifiers() const { return m_functionModifiers; } std::vector> const& returnParameters() const { return m_returnParameters->parameters(); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8c97f55fd6eb..e5cc69d89e88 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -238,7 +238,10 @@ ASTPointer Parser::parseContractDefinition(Token::Value _exp Token::Value currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; - else if (currentTokenValue == Token::Function) + else if ( + currentTokenValue == Token::Function || + (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor") + ) // This can be a function or a state variable of function type (especially // complicated to distinguish fallback function from function type state variable) subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get())); @@ -333,9 +336,19 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; - expectToken(Token::Function); - if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) - result.name = make_shared(); // anonymous function + + if (m_scanner->currentToken() == Token::Function) + // In case of old style constructors, i.e. functions with the same name as the contract, + // this is set to true later in parseFunctionDefinitionOrFunctionTypeStateVariable. + result.isConstructor = false; + else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + result.isConstructor = true; + else + solAssert(false, "Function or constructor expected."); + m_scanner->next(); + + if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen) + result.name = make_shared(); else result.name = expectIdentifierToken(); VarDeclParserOptions options; @@ -426,12 +439,13 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A } else m_scanner->next(); // just consume the ';' - bool const c_isConstructor = (_contractName && *header.name == *_contractName); + if (_contractName && *header.name == *_contractName) + header.isConstructor = true; return nodeFactory.createNode( header.name, header.visibility, header.stateMutability, - c_isConstructor, + header.isConstructor, docstring, header.parameters, header.modifiers, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 3f780af90ac9..2d0e52e1899b 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -56,6 +56,7 @@ class Parser: public ParserBase /// This struct is shared for parsing a function header and a function type. struct FunctionHeaderParserResult { + bool isConstructor; ASTPointer name; ASTPointer parameters; ASTPointer returnParameters; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcdc15199c01..874cc0a63c29 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -962,6 +962,41 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(new_constructor_syntax) +{ + char const* text = R"( + contract A { constructor() public {} } + )"; + CHECK_SUCCESS(text); + + text = R"( + pragma experimental "v0.5.0"; + contract A { constructor() public {} } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(old_constructor_syntax) +{ + char const* text = R"( + contract A { function A() public {} } + )"; + CHECK_WARNING( + text, + "Defining constructors as functions with the same name as the contract is deprecated." + ); + + text = R"( + pragma experimental "v0.5.0"; + contract A { function A() public {} } + )"; + CHECK_ERROR( + text, + SyntaxError, + "Functions are not allowed to have the same name as the contract." + ); +} + BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) { char const* text = R"( @@ -6916,7 +6951,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor) { char const* text = R"( contract C { - function C() public {} + constructor() public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -7328,7 +7363,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) { char const* text = R"( contract C { - function C() public { + constructor() public { this.f(); } function f() pure public { diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index 76df0657fae8..9607ed60caa8 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -1,5 +1,5 @@ contract Base { - function Base(uint) public {} + constructor(uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol index 82aba308840b..45a0770f3567 100644 --- a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -1,9 +1,9 @@ contract Base { - function Base(uint, uint) public {} + constructor(uint, uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base { - function Derived2() Base(2) public { } + constructor() Base(2) public { } } // ---- // TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2. From 07c74ef9241b1fe4fdec5cf4bd97e2c1aedb8d0d Mon Sep 17 00:00:00 2001 From: bitshift Date: Fri, 2 Mar 2018 16:44:35 +0100 Subject: [PATCH 221/242] Updates docs to new constructor syntax. --- docs/contracts.rst | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 8cc4f6b2b3ac..c4eda8dcf8d8 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.16; + pragma solidity ^0.4.20; // should actually be 0.4.21 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) public { + constructor(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, @@ -976,9 +976,30 @@ virtual method lookup. Constructors ============ -A constructor is an optional function with the same name as the contract which is executed upon contract creation. +A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation. Constructor functions can be either ``public`` or ``internal``. +:: + + pragma solidity ^0.4.20; // should actually be 0.4.21 + + contract A { + uint public a; + + constructor(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + constructor() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. + +.. note :: + Prior to version 0.4.21, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. + :: pragma solidity ^0.4.11; @@ -995,7 +1016,6 @@ Constructor functions can be either ``public`` or ``internal``. function B() public {} } -A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. .. index:: ! base;constructor @@ -1009,11 +1029,11 @@ the base constructors. This can be done in two ways:: contract Base { uint x; - function Base(uint _x) public { x = _x; } + constructor(uint _x) public { x = _x; } } contract Derived is Base(7) { - function Derived(uint _y) Base(_y * _y) public { + constructor(uint _y) Base(_y * _y) public { } } From c98464db0607f62aa791aa1f7559a76623e254e7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 16:35:31 +0100 Subject: [PATCH 222/242] Remove redundant test and enforce success without warnings. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 874cc0a63c29..b6596327cc94 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -967,13 +967,7 @@ BOOST_AUTO_TEST_CASE(new_constructor_syntax) char const* text = R"( contract A { constructor() public {} } )"; - CHECK_SUCCESS(text); - - text = R"( - pragma experimental "v0.5.0"; - contract A { constructor() public {} } - )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(old_constructor_syntax) From f855c78a0839d6f569cb88eda290439b69a680e3 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 17:37:47 +0100 Subject: [PATCH 223/242] Update version pragma and use new constructor syntax in std/ contracts. --- docs/contracts.rst | 6 +++--- std/StandardToken.sol | 4 ++-- std/owned.sol | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index c4eda8dcf8d8..e3f5bbace2d2 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.20; // should actually be 0.4.21 + pragma solidity >0.4.21; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -981,7 +981,7 @@ Constructor functions can be either ``public`` or ``internal``. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity >0.4.21; contract A { uint public a; @@ -998,7 +998,7 @@ Constructor functions can be either ``public`` or ``internal``. A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. .. note :: - Prior to version 0.4.21, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. + Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. :: diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 1b218d67d1e8..5afc9747c31c 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; import "./Token.sol"; @@ -8,7 +8,7 @@ contract StandardToken is Token { mapping (address => mapping (address => uint256)) m_allowance; - function StandardToken(address _initialOwner, uint256 _supply) public { + constructor(address _initialOwner, uint256 _supply) public { supply = _supply; balance[_initialOwner] = _supply; } diff --git a/std/owned.sol b/std/owned.sol index ee9860d343af..8e1d59172f9a 100644 --- a/std/owned.sol +++ b/std/owned.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; contract owned { address owner; @@ -9,7 +9,7 @@ contract owned { } } - function owned() public { + constructor() public { owner = msg.sender; } } From e2dac9ed397c29bfe426912c28ef2d419b2324c8 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 7 Mar 2018 16:06:09 +0100 Subject: [PATCH 224/242] Set header.isConstructor for old style constructors in parseFunctionHeader as well. --- libsolidity/parsing/Parser.cpp | 16 +++++++++++----- libsolidity/parsing/Parser.h | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e5cc69d89e88..6ae66eee4443 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -332,14 +332,18 @@ StateMutability Parser::parseStateMutability(Token::Value _token) return stateMutability; } -Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) +Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName +) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; if (m_scanner->currentToken() == Token::Function) // In case of old style constructors, i.e. functions with the same name as the contract, - // this is set to true later in parseFunctionDefinitionOrFunctionTypeStateVariable. + // this is set to true below. result.isConstructor = false; else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") result.isConstructor = true; @@ -351,6 +355,10 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN result.name = make_shared(); else result.name = expectIdentifierToken(); + + if (!result.name->empty() && _contractName && *result.name == *_contractName) + result.isConstructor = true; + VarDeclParserOptions options; options.allowLocationSpecifier = true; result.parameters = parseParameterList(options); @@ -420,7 +428,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A if (m_scanner->currentCommentLiteral() != "") docstring = make_shared(m_scanner->currentCommentLiteral()); - FunctionHeaderParserResult header = parseFunctionHeader(false, true); + FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); if ( !header.modifiers.empty() || @@ -439,8 +447,6 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A } else m_scanner->next(); // just consume the ';' - if (_contractName && *header.name == *_contractName) - header.isConstructor = true; return nodeFactory.createNode( header.name, header.visibility, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 2d0e52e1899b..eb120a6149ef 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -74,7 +74,11 @@ class Parser: public ParserBase ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); StateMutability parseStateMutability(Token::Value _token); - FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + FunctionHeaderParserResult parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName = nullptr + ); ASTPointer parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer parseFunctionDefinition(ASTString const* _contractName); ASTPointer parseStructDefinition(); From 8f66390f56c866f70ab6617dac1f0713ace2d7ff Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 14:23:48 +0100 Subject: [PATCH 225/242] Set isConstructor to false unconditionally and update to true later for constructors. --- libsolidity/parsing/Parser.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6ae66eee4443..3dbd4c8f6aa6 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -341,13 +341,11 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; - if (m_scanner->currentToken() == Token::Function) - // In case of old style constructors, i.e. functions with the same name as the contract, - // this is set to true below. - result.isConstructor = false; - else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + result.isConstructor = false; + + if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") result.isConstructor = true; - else + else if (m_scanner->currentToken() != Token::Function) solAssert(false, "Function or constructor expected."); m_scanner->next(); From 3ae326139a505bed877d5b9ac9b4b3ed84496c3d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 14:25:57 +0100 Subject: [PATCH 226/242] Document absence of constructors. --- docs/contracts.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index e3f5bbace2d2..9ae802093888 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -977,7 +977,9 @@ virtual method lookup. Constructors ============ A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation. -Constructor functions can be either ``public`` or ``internal``. +Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the +default constructor: ``contructor() public {}``. + :: From 17bcabb6cfb42a3983ecb79768d44622507ce292 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 29 Mar 2018 12:10:39 +0100 Subject: [PATCH 227/242] Remove useless SWAP1 in front of commutative operations --- Changelog.md | 1 + libevmasm/PeepholeOptimiser.cpp | 21 +++++++++++++- test/libevmasm/Optimiser.cpp | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 34c3b0e98d0a..cdfdf6aed049 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 168d11090ef4..3064654532df 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -154,6 +154,25 @@ struct DoublePush: SimplePeepholeOptimizerMethod } }; +struct CommutativeSwap: SimplePeepholeOptimizerMethod +{ + static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + { + // Remove SWAP1 if following instruction is commutative + if ( + _swap.type() == Operation && + _swap.instruction() == Instruction::SWAP1 && + SemanticInformation::isCommutativeOperation(_op) + ) + { + *_out = _op; + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -260,7 +279,7 @@ bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) - applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) || diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 28f6b8c0f6d5..6b588e753274 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -858,6 +858,57 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) BOOST_CHECK(items.empty()); } +BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) +{ + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::ADD, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::ADD, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + +BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) +{ + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::SUB, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::SUB, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(!peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + BOOST_AUTO_TEST_CASE(jumpdest_removal) { AssemblyItems items{ From be6051beadcc54705f35738d37af017d654c2c01 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Apr 2018 18:05:06 +0200 Subject: [PATCH 228/242] Test multiple instructions with the (non)commutative peephole optimiser --- test/libevmasm/Optimiser.cpp | 99 ++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 6b588e753274..b622b4fb9dff 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -860,53 +860,78 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) { - AssemblyItems items{ - u256(1), - u256(2), - Instruction::SWAP1, - Instruction::ADD, - u256(4), - u256(5) - }; - AssemblyItems expectation{ - u256(1), - u256(2), + vector ops{ Instruction::ADD, - u256(4), - u256(5) + Instruction::MUL, + Instruction::EQ, + Instruction::AND, + Instruction::OR, + Instruction::XOR }; - PeepholeOptimiser peepOpt(items); - BOOST_REQUIRE(peepOpt.optimise()); - BOOST_CHECK_EQUAL_COLLECTIONS( + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), - expectation.begin(), expectation.end() - ); + expectation.begin(), expectation.end() + ); + } } BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) { - AssemblyItems items{ - u256(1), - u256(2), - Instruction::SWAP1, + // NOTE: not comprehensive + vector ops{ Instruction::SUB, - u256(4), - u256(5) - }; - AssemblyItems expectation{ - u256(1), - u256(2), - Instruction::SWAP1, - Instruction::SUB, - u256(4), - u256(5) + Instruction::DIV, + Instruction::SDIV, + Instruction::MOD, + Instruction::SMOD, + Instruction::EXP, + Instruction::LT, + Instruction::GT }; - PeepholeOptimiser peepOpt(items); - BOOST_REQUIRE(!peepOpt.optimise()); - BOOST_CHECK_EQUAL_COLLECTIONS( + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(!peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), - expectation.begin(), expectation.end() - ); + expectation.begin(), expectation.end() + ); + } } BOOST_AUTO_TEST_CASE(jumpdest_removal) From 6f9644add18b27363a423e2c7ccb0e578ba800bc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Apr 2018 12:05:26 +0200 Subject: [PATCH 229/242] SyntaxTests: extend syntax tests and isoltest to support parser errors and compiler exceptions. --- test/libsolidity/AnalysisFramework.cpp | 28 ++++---- test/libsolidity/AnalysisFramework.h | 7 +- test/libsolidity/SyntaxTest.cpp | 98 ++++++++------------------ test/libsolidity/SyntaxTest.h | 31 ++++---- test/tools/isoltest.cpp | 65 ++++++++++------- 5 files changed, 104 insertions(+), 125 deletions(-) diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 4538757de5b4..72b86767371c 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -56,12 +56,23 @@ AnalysisFramework::parseAnalyseAndReturnError( m_compiler.analyze(); + ErrorList errors = filterErrors(m_compiler.errors(), _reportWarnings); + if (errors.size() > 1 && !_allowMultipleErrors) + BOOST_FAIL("Multiple errors found: " + formatErrors()); + + return make_pair(&m_compiler.ast(""), std::move(errors)); +} + +ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarnings) const +{ ErrorList errors; - for (auto const& currentError: m_compiler.errors()) + for (auto const& currentError: _errorList) { solAssert(currentError->comment(), ""); if (currentError->type() == Error::Type::Warning) { + if (!_includeWarnings) + continue; bool ignoreWarning = false; for (auto const& filter: m_warningsToFilter) if (currentError->comment()->find(filter) == 0) @@ -73,17 +84,10 @@ AnalysisFramework::parseAnalyseAndReturnError( continue; } - if (_reportWarnings || (currentError->type() != Error::Type::Warning)) - { - if (!_allowMultipleErrors && !errors.empty()) - { - BOOST_FAIL("Multiple errors found: " + formatErrors()); - } - errors.emplace_back(std::move(currentError)); - } + errors.emplace_back(currentError); } - return make_pair(&m_compiler.ast(""), errors); + return errors; } SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) @@ -110,7 +114,7 @@ ErrorList AnalysisFramework::expectError(std::string const& _source, bool _warni return sourceAndErrors.second; } -string AnalysisFramework::formatErrors() +string AnalysisFramework::formatErrors() const { string message; for (auto const& error: m_compiler.errors()) @@ -118,7 +122,7 @@ string AnalysisFramework::formatErrors() return message; } -string AnalysisFramework::formatError(Error const& _error) +string AnalysisFramework::formatError(Error const& _error) const { return SourceReferenceFormatter::formatExceptionInformation( _error, diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 6ecf4a5aaae4..05490a42af2f 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -57,8 +57,8 @@ class AnalysisFramework bool success(std::string const& _source); ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); - std::string formatErrors(); - std::string formatError(Error const& _error); + std::string formatErrors() const; + std::string formatError(Error const& _error) const; static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); static FunctionTypePointer retrieveFunctionBySignature( @@ -66,6 +66,9 @@ class AnalysisFramework std::string const& _signature ); + // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false + ErrorList filterErrors(ErrorList const& _errorList, bool _includeWarnings) const; + std::vector m_warningsToFilter = {"This is a pre-release compiler version"}; dev::solidity::CompilerStack m_compiler; }; diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ca0511384286..329543bfbf45 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -59,93 +60,52 @@ SyntaxTest::SyntaxTest(string const& _filename) bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; - if (!matchesExpectations(m_errorList)) + m_compiler.reset(); + m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); + + if (m_compiler.parse()) + m_compiler.analyze(); + + for (auto const& currentError: filterErrors(m_compiler.errors(), true)) + m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)}); + + if (m_expectations != m_errorList) { - std::string nextIndentLevel = _linePrefix + " "; + string nextIndentLevel = _linePrefix + " "; FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel, _formatted); + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const -{ - if (m_expectations.empty()) - FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; - else - for (auto const& expectation: m_expectations) - { - FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": "; - _stream << expectation.message << endl; - } -} - void SyntaxTest::printErrorList( ostream& _stream, - ErrorList const& _errorList, + vector const& _errorList, string const& _linePrefix, - bool const _ignoreWarnings, - bool const _lineNumbers, bool const _formatted -) const +) { if (_errorList.empty()) FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) { - bool isWarning = (error->type() == Error::Type::Warning); - if (isWarning && _ignoreWarnings) continue; - { - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + FormattedScope scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED}); _stream << _linePrefix; - if (_lineNumbers) - { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; - } - _stream << error->typeName() << ": "; + _stream << error.type << ": "; } - _stream << errorMessage(*error) << endl; + _stream << error.message << endl; } } -int SyntaxTest::offsetToLineNumber(int _location) const -{ - // parseAnalyseAndReturnError(...) prepends a version pragma - _location -= strlen("pragma solidity >=0.0;\n"); - if (_location < 0 || static_cast(_location) >= m_source.size()) - return -1; - else - return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); -} - -bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const -{ - if (_errorList.size() != m_expectations.size()) - return false; - else - for (size_t i = 0; i < _errorList.size(); i++) - if ( - (_errorList[i]->typeName() != m_expectations[i].type) || - (errorMessage(*_errorList[i]) != m_expectations[i].message) - ) - return false; - return true; -} - -string SyntaxTest::errorMessage(Error const& _e) +string SyntaxTest::errorMessage(Exception const& _e) { - if (_e.comment()) + if (_e.comment() && !_e.comment()->empty()) return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); else return "NONE"; @@ -164,9 +124,9 @@ string SyntaxTest::parseSource(istream& _stream) return source; } -vector SyntaxTest::parseExpectations(istream& _stream) +vector SyntaxTest::parseExpectations(istream& _stream) { - vector expectations; + vector expectations; string line; while (getline(_stream, line)) { @@ -188,7 +148,7 @@ vector SyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)}); } return expectations; } @@ -239,9 +199,11 @@ int SyntaxTest::registerTests( _suite.add(make_test_case( [fullpath] { - std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream)) - BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + BOOST_REQUIRE_NO_THROW({ + stringstream errorStream; + if (!SyntaxTest(fullpath.string()).run(errorStream)) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + }); }, _path.stem().string(), *filenames.back(), diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index cb6ee05cdbc2..dddd86ef42f9 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -36,10 +36,14 @@ namespace solidity namespace test { -struct SyntaxTestExpectation +struct SyntaxTestError { std::string type; std::string message; + bool operator==(SyntaxTestError const& _rhs) const + { + return type == _rhs.type && message == _rhs.message; + } }; @@ -50,21 +54,16 @@ class SyntaxTest: AnalysisFramework bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); - std::vector const& expectations() const { return m_expectations; } + std::vector const& expectations() const { return m_expectations; } std::string const& source() const { return m_source; } - ErrorList const& errorList() const { return m_errorList; } - ErrorList const& compilerErrors() const { return m_compiler.errors(); } + std::vector const& errorList() const { return m_errorList; } - void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const; - - void printErrorList( + static void printErrorList( std::ostream& _stream, - ErrorList const& _errors, + std::vector const& _errors, std::string const& _linePrefix, - bool const _ignoreWarnings, - bool const _lineNumbers, bool const _formatted = false - ) const; + ); static int registerTests( boost::unit_test::test_suite& _suite, @@ -72,16 +71,14 @@ class SyntaxTest: AnalysisFramework boost::filesystem::path const& _path ); static bool isTestFilename(boost::filesystem::path const& _filename); + static std::string errorMessage(Exception const& _e); private: - bool matchesExpectations(ErrorList const& _errors) const; - static std::string errorMessage(Error const& _e); static std::string parseSource(std::istream& _stream); - static std::vector parseExpectations(std::istream& _stream); - int offsetToLineNumber(int _location) const; + static std::vector parseExpectations(std::istream& _stream); std::string m_source; - std::vector m_expectations; - ErrorList m_errorList; + std::vector m_expectations; + std::vector m_errorList; }; } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 5efec4211667..07df8f60af50 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -55,8 +55,8 @@ class SyntaxTestTool { Success, Failure, - ParserError, - InputOutputError + InputOutputError, + Exception }; Result process(); @@ -76,7 +76,7 @@ class SyntaxTestTool Quit }; - Request handleResponse(bool const _parserError); + Request handleResponse(bool const _exception); void printContract() const; @@ -100,7 +100,6 @@ void SyntaxTestTool::printContract() const SyntaxTestTool::Result SyntaxTestTool::process() { bool success; - bool parserError = false; std::stringstream outputMessages; (FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush(); @@ -119,10 +118,35 @@ SyntaxTestTool::Result SyntaxTestTool::process() { success = m_test->run(outputMessages, " ", m_formatted); } - catch (...) + catch(CompilerError const& _e) { - success = false; - parserError = true; + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "Exception: " << SyntaxTest::errorMessage(_e) << endl; + return Result::Exception; + } + catch(InternalCompilerError const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "InternalCompilerError: " << SyntaxTest::errorMessage(_e) << endl; + return Result::Exception; + } + catch(FatalError const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "FatalError: " << SyntaxTest::errorMessage(_e) << endl; + return Result::Exception; + } + catch(UnimplementedFeatureError const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl; + return Result::Exception; + } + catch(...) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "Unknown Exception" << endl; + return Result::Exception; } if (success) @@ -137,25 +161,14 @@ SyntaxTestTool::Result SyntaxTestTool::process() FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; printContract(); - if (parserError) - { - cout << " "; - FormattedScope(cout, m_formatted, {INVERSE, RED}) << "Parsing failed:" << endl; - m_test->printErrorList(cout, m_test->compilerErrors(), " ", true, true, m_formatted); - cout << endl; - return Result::ParserError; - } - else - { - cout << outputMessages.str() << endl; - return Result::Failure; - } + cout << outputMessages.str() << endl; + return Result::Failure; } } -SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) +SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) { - if (_parserError) + if (_exception) cout << "(e)dit/(s)kip/(q)uit? "; else cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? "; @@ -169,7 +182,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) cout << endl; return Request::Skip; case 'u': - if (_parserError) + if (_exception) break; else { @@ -178,7 +191,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) file << m_test->source(); file << "// ----" << endl; if (!m_test->errorList().empty()) - m_test->printErrorList(file, m_test->errorList(), "// ", false, false, false); + m_test->printErrorList(file, m_test->errorList(), "// ", false); return Request::Rerun; } case 'e': @@ -231,8 +244,8 @@ SyntaxTestStats SyntaxTestTool::processPath( switch(result) { case Result::Failure: - case Result::ParserError: - switch(testTool.handleResponse(result == Result::ParserError)) + case Result::Exception: + switch(testTool.handleResponse(result == Result::Exception)) { case Request::Quit: return { successCount, runCount }; From 87ad337ae0c055c21476ef9961e52a9d7d3072bb Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Apr 2018 14:41:53 +0200 Subject: [PATCH 230/242] Adds parsing sub-directory in syntaxTests and moves two example tests from SolidityParser.cpp to test contracts. --- test/libsolidity/SolidityParser.cpp | 20 ------------------- .../missing_variable_name_in_declaration.sol | 5 +++++ .../syntaxTests/parsing/smoke_test.sol | 4 ++++ 3 files changed, 9 insertions(+), 20 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol create mode 100644 test/libsolidity/syntaxTests/parsing/smoke_test.sol diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 4e862f608ad4..93e6bcaa48bd 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -112,26 +112,6 @@ while(0) BOOST_AUTO_TEST_SUITE(SolidityParser) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* text = R"( - contract test { - uint256 stateVariable1; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) -{ - char const* text = R"( - contract test { - uint256 ; - } - )"; - CHECK_PARSE_ERROR(text, "Expected identifier"); -} - BOOST_AUTO_TEST_CASE(empty_function) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol new file mode 100644 index 000000000000..c03fd97d1098 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol @@ -0,0 +1,5 @@ +contract test { + uint256 ; +} +// ---- +// ParserError: Expected identifier, got 'Semicolon' diff --git a/test/libsolidity/syntaxTests/parsing/smoke_test.sol b/test/libsolidity/syntaxTests/parsing/smoke_test.sol new file mode 100644 index 000000000000..d328b1675167 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/smoke_test.sol @@ -0,0 +1,4 @@ +contract test { + uint256 stateVariable1; +} +// ---- From 86c5d6aaadf16e823ad7793e90a564cda2a10a4c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 4 Apr 2018 09:02:58 +0300 Subject: [PATCH 231/242] hash256 -> bytes32 fix in misleading note As it was described here: https://ethereum.stackexchange.com/questions/44628/understanding-low-level-interface-to-logs/44629?noredirect=1#comment52316_44629 --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 9ae802093888..f8a44fb3bcdd 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -803,7 +803,7 @@ as topics. The event call above can be performed in the same way as } where the long hexadecimal number is equal to -``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event. +``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. Additional Resources for Understanding Events ============================================== From fa92380af05219cc604ddb1a9a9fadcfe76097c8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 12:26:34 +0200 Subject: [PATCH 232/242] Add scripts to build the eth binary via docker. --- scripts/cpp-ethereum/build.sh | 17 +++++++++++++++++ scripts/cpp-ethereum/eth_artful.docker | 7 +++++++ scripts/cpp-ethereum/eth_trusty.docker | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100755 scripts/cpp-ethereum/build.sh create mode 100644 scripts/cpp-ethereum/eth_artful.docker create mode 100644 scripts/cpp-ethereum/eth_trusty.docker diff --git a/scripts/cpp-ethereum/build.sh b/scripts/cpp-ethereum/build.sh new file mode 100755 index 000000000000..23ed129069a4 --- /dev/null +++ b/scripts/cpp-ethereum/build.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +# Script to build the eth binary from latest develop +# for ubuntu trusty and ubuntu artful. +# Requires docker. + +set -e + +REPO_ROOT="$(dirname "$0")"/../.. + +for rel in artful trusty +do + docker build -t eth_$rel -f "$REPO_ROOT"/scripts/cpp-ethereum/eth_$rel.docker . + tmp_container=$(docker create eth_$rel sh) + echo "Built eth ($rel) at $REPO_ROOT/build/eth_$rel" + docker cp ${tmp_container}:/build/eth/eth "$REPO_ROOT"/build/eth_$rel +done \ No newline at end of file diff --git a/scripts/cpp-ethereum/eth_artful.docker b/scripts/cpp-ethereum/eth_artful.docker new file mode 100644 index 000000000000..7ce9faae89da --- /dev/null +++ b/scripts/cpp-ethereum/eth_artful.docker @@ -0,0 +1,7 @@ +FROM ubuntu:artful + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker new file mode 100644 index 000000000000..0c4d7a02cda7 --- /dev/null +++ b/scripts/cpp-ethereum/eth_trusty.docker @@ -0,0 +1,7 @@ +FROM ubuntu:trusty + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth From 0cbe55005de79b0f7c5c770d50c3eb87df019789 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 15:21:38 +0100 Subject: [PATCH 233/242] Create empty dynamic memory arrays more efficiently. --- docs/assembly.rst | 5 ++++ docs/miscellaneous.rst | 11 ++++--- libsolidity/codegen/CompilerUtils.cpp | 30 ++++++++++++++----- libsolidity/codegen/CompilerUtils.h | 7 +++++ test/libsolidity/JSONCompiler.cpp | 8 ++--- test/libsolidity/SolidityEndToEndTest.cpp | 36 ++++++++++++++++++++++- test/libsolidity/SolidityOptimizer.cpp | 24 +++++++++++++++ test/libsolidity/StandardCompiler.cpp | 6 ++-- 8 files changed, 107 insertions(+), 20 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index cf9bf840fde0..705cd1b8451d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -647,6 +647,11 @@ Solidity manages memory in a very simple way: There is a "free memory pointer" at position ``0x40`` in memory. If you want to allocate memory, just use the memory from that point on and update the pointer accordingly. +The first 64 bytes of memory can be used as "scratch space" for short-term +allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``) +is meant to be zero permanently and is used as the initial value for +empty dynamic memory arrays. + Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 01154854ceeb..20400aa26a59 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -64,12 +64,15 @@ The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25 Layout in Memory **************** -Solidity reserves three 256-bit slots: +Solidity reserves four 32 byte slots: -- 0 - 64: scratch space for hashing methods -- 64 - 96: currently allocated memory size (aka. free memory pointer) +- ``0x00`` - ``0x3f``: scratch space for hashing methods +- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer) +- ``0x60`` - ``0x7f``: zero slot -Scratch space can be used between statements (ie. within inline assembly). +Scratch space can be used between statements (ie. within inline assembly). The zero slot +is used as initial value for dynamic memory arrays and should never be written to +(the free memory pointer points to ``0x80`` initially). Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index deaef017adf6..79aef7b06ba5 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -21,6 +21,7 @@ */ #include + #include #include #include @@ -39,11 +40,17 @@ namespace solidity const unsigned CompilerUtils::dataStartOffset = 4; const size_t CompilerUtils::freeMemoryPointer = 64; +const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; +const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; const unsigned CompilerUtils::identityContractAddress = 4; +static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area."); +static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer."); +static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area."); + void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(freeMemoryPointer + 32); + m_context << u256(generalPurposeMemoryStart); storeFreeMemoryPointer(); } @@ -1051,6 +1058,13 @@ void CompilerUtils::pushZeroValue(Type const& _type) return; } solAssert(referenceType->location() == DataLocation::Memory, ""); + if (auto arrayType = dynamic_cast(&_type)) + if (arrayType->isDynamicallySized()) + { + // Push a memory location that is (hopefully) always zero. + pushZeroPointer(); + return; + } TypePointer type = _type.shared_from_this(); m_context.callLowLevelFunction( @@ -1071,13 +1085,8 @@ void CompilerUtils::pushZeroValue(Type const& _type) } else if (auto arrayType = dynamic_cast(type.get())) { - if (arrayType->isDynamicallySized()) - { - // zero length - _context << u256(0); - utils.storeInMemoryDynamic(IntegerType(256)); - } - else if (arrayType->length() > 0) + solAssert(!arrayType->isDynamicallySized(), ""); + if (arrayType->length() > 0) { _context << arrayType->length() << Instruction::SWAP1; // stack: items_to_do memory_pos @@ -1094,6 +1103,11 @@ void CompilerUtils::pushZeroValue(Type const& _type) ); } +void CompilerUtils::pushZeroPointer() +{ + m_context << u256(zeroPointer); +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable)); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 389673ef06ea..a32c5c6e1df7 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -210,6 +210,9 @@ class CompilerUtils /// Creates a zero-value for the given type and puts it onto the stack. This might allocate /// memory for memory references. void pushZeroValue(Type const& _type); + /// Pushes a pointer to the stack that points to a (potentially shared) location in memory + /// that always contains a zero. It is not allowed to write there. + void pushZeroPointer(); /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); @@ -255,6 +258,10 @@ class CompilerUtils /// Position of the free-memory-pointer in memory; static const size_t freeMemoryPointer; + /// Position of the memory slot that is always zero. + static const size_t zeroPointer; + /// Starting offset for memory available to the user (aka the contract). + static const size_t generalPurposeMemoryStart; private: /// Address of the precompiled identity contract. diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index aed0a370658a..cdcc22a650a8 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -111,12 +111,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); @@ -153,12 +153,12 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 38d3ce4dfe64..b58ebee455bd 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7687,7 +7687,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256)); } - BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) { // Computes binomial coefficients the chinese way @@ -7710,6 +7709,41 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70))); } +BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + uint[][] memory x = new uint[][](42); + assert(x[0].length == 0); + x[0] = new uint[](1); + x[0][0] = 1; + assert(x[4].length == 0); + x[4] = new uint[](1); + x[4][0] = 2; + assert(x[10].length == 0); + x[10] = new uint[](1); + x[10][0] = 44; + uint[][] memory y = new uint[][](24); + assert(y[0].length == 0); + y[0] = new uint[](1); + y[0][0] = 1; + assert(y[4].length == 0); + y[4] = new uint[](1); + y[4][0] = 2; + assert(y[10].length == 0); + y[10] = new uint[](1); + y[10][0] = 88; + if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88)) + return 7; + return 0; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index cf4550c718dd..a0df76f33ff2 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -93,8 +93,10 @@ class OptimizerTestFramework: public SolidityExecutionFramework { m_contractAddress = m_nonOptimizedContract; bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedNonOptimized = m_gasUsed; m_contractAddress = m_optimizedContract; bytes optimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedOptimized = m_gasUsed; BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig); BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig); BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match." @@ -120,6 +122,8 @@ class OptimizerTestFramework: public SolidityExecutionFramework } protected: + u256 m_gasUsedOptimized; + u256 m_gasUsedNonOptimized; bytes m_nonOptimizedBytecode; bytes m_optimizedBytecode; Address m_optimizedContract; @@ -584,6 +588,26 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) compareVersions("test()"); } +BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays) +{ + // This is not so much an optimizer test, but rather a test + // that allocating empty arrays is implemented efficiently. + // In particular, initializing a dynamic memory array does + // not use any memory. + char const* sourceCode = R"( + contract Test { + function f() pure returns (uint r) { + uint[][] memory x = new uint[][](20000); + return x.length; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); + BOOST_CHECK_LE(m_gasUsedNonOptimized, 1900000); + BOOST_CHECK_LE(1600000, m_gasUsedNonOptimized); +} + BOOST_AUTO_TEST_CASE(optimise_multi_stores) { char const* sourceCode = R"( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index dd6eb7c4005f..b285a2a0e99f 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -261,14 +261,14 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( - " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" + " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n jumpi(tag_1, iszero(callvalue))\n" " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n" + " mstore(0x40, 0x80)\n 0x0\n dup1\n revert\n\n" " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( From 0f7e18780f9afb1552feaf2100bde70aace7b2bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 15:07:29 +0200 Subject: [PATCH 234/242] Clarify code state of contracts under construction. --- docs/introduction-to-smart-contracts.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 56f0fe3e54b6..84b1fff8053b 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -326,7 +326,13 @@ EVM bytecode and executed. The output of this execution is permanently stored as the code of the contract. This means that in order to create a contract, you do not send the actual code of the contract, but in fact code that -returns that code. +returns that code when executed. + +.. note:: + While a contract is being created, its code is still empty. + Because of that, you should not call back into the + contract under construction until its constructor has + finished executing. .. index:: ! gas, ! gas price From 65f18a18de410b46844892e69b0578a61669aac1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 15:38:14 +0100 Subject: [PATCH 235/242] More specific push implementation. --- Changelog.md | 1 + libsolidity/codegen/ArrayUtils.cpp | 49 ++++++++++++++++ libsolidity/codegen/ArrayUtils.h | 6 ++ libsolidity/codegen/ExpressionCompiler.cpp | 29 +++++----- test/libsolidity/SolidityEndToEndTest.cpp | 65 ++++++++++++++++++++++ test/libsolidity/SolidityOptimizer.cpp | 4 +- 6 files changed, 139 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9618dfa74558..0db5df9603bd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Code Generator: Initialize arrays without using ``msize()``. + * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 4703fc1f64c4..0fe66d2d90a1 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ); } +void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const +{ + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + + if (_type.isByteArray()) + { + // We almost always just add 2 (length of byte arrays is shifted left by one) + // except for the case where we transition from a short byte array + // to a long byte array, there we have to copy. + // This happens if the length is exactly 31, which means that the + // lowest-order byte (we actually use a mask with fewer bits) must + // be (31*2+0) = 62 + + m_context.appendInlineAssembly(R"({ + let data := sload(ref) + let shifted_length := and(data, 63) + // We have to copy if length is exactly 31, because that marks + // the transition between in-place and out-of-place storage. + switch shifted_length + case 62 + { + mstore(0, ref) + let data_area := keccak256(0, 0x20) + sstore(data_area, and(data, not(0xff))) + // New length is 32, encoded as (32 * 2 + 1) + sstore(ref, 65) + // Replace ref variable by new length + ref := 32 + } + default + { + sstore(ref, add(data, 2)) + // Replace ref variable by new length + if iszero(and(data, 1)) { data := shifted_length } + ref := add(div(data, 2), 1) + } + })", {"ref"}); + } + else + m_context.appendInlineAssembly(R"({ + let new_length := add(sload(ref), 1) + sstore(ref, new_length) + ref := new_length + })", {"ref"}); +} + void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { m_context.callLowLevelFunction( diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index f3ddc4ee84a9..99786397d61c 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -67,6 +67,12 @@ class ArrayUtils /// Stack pre: reference (excludes byte offset) new_length /// Stack post: void resizeDynamicArray(ArrayType const& _type) const; + /// Increments the size of a dynamic array by one. + /// Does not touch the new data element. In case of a byte array, this might move the + /// data. + /// Stack pre: reference (excludes byte offset) + /// Stack post: new_length + void incrementDynamicArraySize(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 76aa68438bc3..57d49ac66ad4 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -821,24 +821,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) function.kind() == FunctionType::Kind::ArrayPush ? make_shared(DataLocation::Storage, paramType) : make_shared(DataLocation::Storage); - // get the current length - ArrayUtils(m_context).retrieveLength(*arrayType); - m_context << Instruction::DUP1; - // stack: ArrayReference currentLength currentLength - m_context << u256(1) << Instruction::ADD; - // stack: ArrayReference currentLength newLength - m_context << Instruction::DUP3 << Instruction::DUP2; - ArrayUtils(m_context).resizeDynamicArray(*arrayType); - m_context << Instruction::SWAP2 << Instruction::SWAP1; - // stack: newLength ArrayReference oldLength - ArrayUtils(m_context).accessIndex(*arrayType, false); - // stack: newLength storageSlot slotOffset + // stack: ArrayReference arguments[0]->accept(*this); + TypePointer const& argType = arguments[0]->annotation().type; + // stack: ArrayReference argValue + utils().moveToStackTop(argType->sizeOnStack(), 1); + // stack: argValue ArrayReference + m_context << Instruction::DUP1; + ArrayUtils(m_context).incrementDynamicArraySize(*arrayType); + // stack: argValue ArrayReference newLength + m_context << Instruction::SWAP1; + // stack: argValue newLength ArrayReference + m_context << u256(1) << Instruction::DUP3 << Instruction::SUB; + // stack: argValue newLength ArrayReference (newLength-1) + ArrayUtils(m_context).accessIndex(*arrayType, false); + // stack: argValue newLength storageSlot slotOffset + utils().moveToStackTop(3, argType->sizeOnStack()); // stack: newLength storageSlot slotOffset argValue TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); solAssert(type, ""); - utils().convertType(*arguments[0]->annotation().type, *type); + utils().convertType(*argType, *type); utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack()); // stack: newLength argValue storageSlot slotOffset diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b58ebee455bd..07aa437edcb5 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4884,6 +4884,48 @@ BOOST_AUTO_TEST_CASE(array_push) ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3)); } +BOOST_AUTO_TEST_CASE(array_push_struct) +{ + char const* sourceCode = R"( + contract c { + struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; } + S[] data; + function test() returns (uint16, uint16, uint16, uint16) { + S memory s; + s.a = 2; + s.b = 3; + s.c[2] = 4; + s.d = new uint16[](4); + s.d[2] = 5; + data.push(s); + return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5)); +} + +BOOST_AUTO_TEST_CASE(array_push_packed_array) +{ + char const* sourceCode = R"( + contract c { + uint80[] x; + function test() returns (uint80, uint80, uint80, uint80) { + x.push(1); + x.push(2); + x.push(3); + x.push(4); + x.push(5); + x.length = 4; + return (x[0], x[1], x[2], x[3]); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4)); +} + BOOST_AUTO_TEST_CASE(byte_array_push) { char const* sourceCode = R"( @@ -4904,6 +4946,29 @@ BOOST_AUTO_TEST_CASE(byte_array_push) ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); } +BOOST_AUTO_TEST_CASE(byte_array_push_transition) +{ + // Tests transition between short and long encoding + char const* sourceCode = R"( + contract c { + bytes data; + function test() returns (uint) { + for (uint i = 1; i < 40; i++) + { + data.push(byte(i)); + if (data.length != i) return 0x1000 + i; + if (data[data.length - 1] != byte(i)) return i; + } + for (i = 1; i < 40; i++) + if (data[i - 1] != byte(i)) return 0x1000000 + i; + return 0; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index a0df76f33ff2..5326feaf0e7b 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -627,8 +627,8 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) )"; compileBothVersions(sourceCode); compareVersions("f()"); - BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 13); - BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 11); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 8); } BOOST_AUTO_TEST_SUITE_END() From 02ea0e547f3faa687f9c986ca9a0201b995846c0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 4 Apr 2018 15:28:15 +0200 Subject: [PATCH 236/242] Replace comparison operators with opposites if preceded by SWAP1 --- Changelog.md | 1 + libevmasm/PeepholeOptimiser.cpp | 28 ++++++++++++++++++++- test/libevmasm/Optimiser.cpp | 43 +++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9618dfa74558..1d854291076b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). + * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 3064654532df..8a39de247fee 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -173,6 +173,32 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod } }; +struct SwapComparison: SimplePeepholeOptimizerMethod +{ + static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + { + map swappableOps{ + { Instruction::LT, Instruction::GT }, + { Instruction::GT, Instruction::LT }, + { Instruction::SLT, Instruction::SGT }, + { Instruction::SGT, Instruction::SLT } + }; + + if ( + _swap.type() == Operation && + _swap.instruction() == Instruction::SWAP1 && + _op.type() == Operation && + swappableOps.count(_op.instruction()) + ) + { + *_out = swappableOps.at(_op.instruction()); + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -279,7 +305,7 @@ bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) - applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) || diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index b622b4fb9dff..089be45d47a9 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -888,7 +888,7 @@ BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) PeepholeOptimiser peepOpt(items); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( - items.begin(), items.end(), + items.begin(), items.end(), expectation.begin(), expectation.end() ); } @@ -903,9 +903,7 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) Instruction::SDIV, Instruction::MOD, Instruction::SMOD, - Instruction::EXP, - Instruction::LT, - Instruction::GT + Instruction::EXP }; for (Instruction const op: ops) { @@ -928,7 +926,42 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) PeepholeOptimiser peepOpt(items); BOOST_REQUIRE(!peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( - items.begin(), items.end(), + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); + } +} + +BOOST_AUTO_TEST_CASE(peephole_swap_comparison) +{ + map swappableOps{ + { Instruction::LT, Instruction::GT }, + { Instruction::GT, Instruction::LT }, + { Instruction::SLT, Instruction::SGT }, + { Instruction::SGT, Instruction::SLT } + }; + + for (auto const& op: swappableOps) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op.first, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + op.second, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), expectation.begin(), expectation.end() ); } From 8dc9113e370a2edd42213c2c0cca811a3dbd8dd4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 10:41:43 +0200 Subject: [PATCH 237/242] Add end-to-end test for SwapComparison and CommutativeSwap peephole optimisers --- test/libsolidity/SolidityEndToEndTest.cpp | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b58ebee455bd..4b4fc2afbf4c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11032,6 +11032,50 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) } } +BOOST_AUTO_TEST_CASE(swap_peephole_optimisation) +{ + char const* sourceCode = R"( + contract C { + function lt(uint a, uint b) returns (bool c) { + assembly { + a + b + swap1 + lt + =: c + } + } + function add(uint a, uint b) returns (uint c) { + assembly { + a + b + swap1 + add + =: c + } + } + function div(uint a, uint b) returns (uint c) { + assembly { + a + b + swap1 + div + =: c + } + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(100), u256(200)) == encodeArgs(u256(300))); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(200), u256(10)) == encodeArgs(u256(20))); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(1), u256(0)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(0), u256(1)) == encodeArgs(u256(0))); +} + BOOST_AUTO_TEST_SUITE_END() } From acf364f8f20d8596ae6f2abdb54eed95d4f6924d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 12 Feb 2018 23:21:56 +0000 Subject: [PATCH 238/242] Use emscripten 1.37.21 on Travis (same as on Circleci) --- .travis.yml | 2 +- scripts/build_emscripten.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed4584ef587b..31c10bf1a9d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,7 +90,7 @@ matrix: before_install: - nvm install 7 - nvm use 7 - - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit + - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index cddcd4f8636c..14c497aeaf97 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit ./scripts/travis-emscripten/build_emscripten.sh fi From e81dea45b5db9128f72cd765c3c6a28e62bbbfc6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 20 Feb 2018 23:43:46 +0100 Subject: [PATCH 239/242] do emscripten debugging and cache cleaning --- .travis.yml | 3 +++ cmake/EthCompilerSettings.cmake | 1 + 2 files changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 31c10bf1a9d8..26386546a451 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,7 +91,10 @@ matrix: - nvm install 7 - nvm use 7 - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit + - emcc --clear-cache + - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit emcc --clear-cache env: + - EMCC_DEBUG=1 - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off - SOLC_RELEASE=Off diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index a9ed0a743fe3..841b032d0357 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -143,6 +143,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Abort if linking results in any undefined symbols # Note: this is on by default in the CMake Emscripten module which we aren't using set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s VERBOSE=1") add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() From 35248e70dbda3bbb1021dbe7496f36106a5b7b46 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 21 Feb 2018 00:05:59 +0100 Subject: [PATCH 240/242] run emcc only in docker --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 26386546a451..558c0c6bbe41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,7 +91,6 @@ matrix: - nvm install 7 - nvm use 7 - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit - - emcc --clear-cache - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit emcc --clear-cache env: - EMCC_DEBUG=1 From 30ce420c7d3d9a3ba8e3621e8965af1caee0e982 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 21 Feb 2018 00:38:52 +0100 Subject: [PATCH 241/242] f --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 558c0c6bbe41..e3e370a1d6c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,7 +91,7 @@ matrix: - nvm install 7 - nvm use 7 - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit - - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.35.4-64bit emcc --clear-cache + - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit emcc --clear-cache env: - EMCC_DEBUG=1 - SOLC_EMSCRIPTEN=On From 1ad4f4495629ec90f7e7facc44912e4c3254ca75 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 21 Feb 2018 10:05:19 +0100 Subject: [PATCH 242/242] remove cache clearning --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e3e370a1d6c7..e1a25ad50745 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,7 +91,6 @@ matrix: - nvm install 7 - nvm use 7 - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit - - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit emcc --clear-cache env: - EMCC_DEBUG=1 - SOLC_EMSCRIPTEN=On