From c94c77d2d165900cb0926e96764ca4e1a18b7a84 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 30 Jun 2017 19:35:00 +1000 Subject: [PATCH 001/163] Update frequently-asked-questions.rst --- docs/frequently-asked-questions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 03ee8388e639..8eaa5c5affb6 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== -The automatic getter function for a public state variable of array type only returns +The automatic :ref:`getters ` function for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that. From d2c0c5f4621d357f57c9657aad76c3808fa8f554 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 30 Jun 2017 19:42:53 +1000 Subject: [PATCH 002/163] Getter function ref --- docs/frequently-asked-questions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 8eaa5c5affb6..b44480e3070e 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== -The automatic :ref:`getters ` function for a public state variable of array type only returns +The automatic :ref:`getter function ` for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that. From 433039f105448c671e35c4670c79a37ccfd864bc Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 30 Jun 2017 19:47:35 +1000 Subject: [PATCH 003/163] Ref to getter function --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 182de33ad4b2..1b467b5c9650 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -491,7 +491,7 @@ Function Visibility Specifiers return true; } -- ``public``: visible externally and internally (creates getter function for storage/state variables) +- ``public``: visible externally and internally (creates a :ref:`getter function ` for storage/state variables) - ``private``: only visible in the current contract - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``internal``: only visible internally From eb84acbbcb77531b8d544e2ef9c575e12638d62f Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 30 Jun 2017 19:51:10 +1000 Subject: [PATCH 004/163] Ref to getter function --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index dc7c6cc99843..b4674a4315cf 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -133,7 +133,7 @@ too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you added to the mapping or use it in a context where this is not needed, -like this one. The getter function created by the ``public`` keyword +like this one. The :ref:`getter function ` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: From e257a6c89a0cdcb84204f423d8060b33b5f63a5b Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 7 Jul 2017 12:07:10 +1000 Subject: [PATCH 005/163] Fixed reference to getter function --- docs/frequently-asked-questions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index b44480e3070e..53c43633e433 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== -The automatic :ref:`getter function ` for a public state variable of array type only returns +The automatic :ref:`getter function <_getter_functions>` for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that. From ba0d277e103aadd4b43ca3be86a014159d115138 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 7 Jul 2017 12:07:16 +1000 Subject: [PATCH 006/163] Fixed reference to getter function --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index b4674a4315cf..6ad56ff357c7 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -133,7 +133,7 @@ too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you added to the mapping or use it in a context where this is not needed, -like this one. The :ref:`getter function ` created by the ``public`` keyword +like this one. The :ref:`getter function <_getter_functions>` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: From 221a013042ea3133aca743ba127526185df71268 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 7 Jul 2017 12:07:25 +1000 Subject: [PATCH 007/163] Fixed reference to getter function --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 1b467b5c9650..812045a2b50b 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -491,7 +491,7 @@ Function Visibility Specifiers return true; } -- ``public``: visible externally and internally (creates a :ref:`getter function ` for storage/state variables) +- ``public``: visible externally and internally (creates a :ref:`getter function <_getter_functions>` for storage/state variables) - ``private``: only visible in the current contract - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``internal``: only visible internally From f74e40598fc0ca92615acb04bf9895c8c76c2403 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 26 Jul 2017 14:30:32 +0200 Subject: [PATCH 008/163] Clarify internal function types Fixes https://github.com/ethereum/solidity/issues/2617 --- docs/types.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index 0dc436c95bcd..7085610af381 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -318,7 +318,7 @@ can be assigned from functions and function parameters of function type can be used to pass functions to and return functions from function calls. Function types come in two flavours - *internal* and *external* functions: -Internal functions can only be used inside the current contract (more specifically, +Internal functions can only be called inside the current contract (more specifically, inside the current code unit, which also includes internal library functions and inherited functions) because they cannot be executed outside of the context of the current contract. Calling an internal function is realized @@ -337,7 +337,8 @@ function type should not return anything, the whole ``returns ()`` part has to be omitted. By default, function types are internal, so the ``internal`` keyword can be -omitted. +omitted. In contrast, contract functions themselves are public by default, +only when used as the name of a type, the default is internal. There are two ways to access a function in the current contract: Either directly by its name, ``f``, or using ``this.f``. The former will result in an internal From 89551d9968493e1b392c70cf514c84415735d50e Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 11:34:22 +1000 Subject: [PATCH 009/163] Updated reference to getter-functions --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 812045a2b50b..8b88f00cb2fb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -491,7 +491,7 @@ Function Visibility Specifiers return true; } -- ``public``: visible externally and internally (creates a :ref:`getter function <_getter_functions>` for storage/state variables) +- ``public``: visible externally and internally (creates a :ref:`getter function ` for storage/state variables) - ``private``: only visible in the current contract - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``internal``: only visible internally From c03a85aa7b9a88eb9049897d342a3b125b86b7cf Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 11:50:20 +1000 Subject: [PATCH 010/163] ref getter-functions --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 8b88f00cb2fb..493e701bdb85 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -491,7 +491,7 @@ Function Visibility Specifiers return true; } -- ``public``: visible externally and internally (creates a :ref:`getter function ` for storage/state variables) +- ``public``: visible externally and internally (creates a :ref:`getter-functions` for storage/state variables) - ``private``: only visible in the current contract - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``internal``: only visible internally From 3c3060dd870b9971e3f571faca3f9036995306b9 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 11:54:59 +1000 Subject: [PATCH 011/163] :ref:`getter-functions` --- docs/frequently-asked-questions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 53c43633e433..e7ccb3db3822 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== -The automatic :ref:`getter function <_getter_functions>` for a public state variable of array type only returns +The automatic :ref:`getter-functions` for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that. From 84e71f061cb9dc2069ac3acaa50afca8a98860bb Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 15:07:18 +1000 Subject: [PATCH 012/163] Link: members-of-addresses --- docs/types.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/types.rst b/docs/types.rst index dd9c6269f2ef..ebe46b6590ea 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -83,6 +83,8 @@ Operators: * ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` +.. _members-of-addresses: + Members of Addresses ^^^^^^^^^^^^^^^^^^^^ From c5b82f7d50d2f6040eea3dd71ebe4d759b9703e6 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 15:24:19 +1000 Subject: [PATCH 013/163] Constructor index For referencing, especially when it hasn't been mentioned but not discussed in detail previously e.g. here and here. --- docs/contracts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 4c3d40597139..4e33c5f32dfd 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -10,7 +10,7 @@ variables. Calling a function on a different contract (instance) will perform an EVM function call and thus switch the context such that state variables are inaccessible. -.. index:: ! contract;creation +.. index:: ! contract;creation, constructor ****************** Creating Contracts @@ -20,7 +20,7 @@ Contracts can be created "from outside" or from Solidity contracts. When a contract is created, its constructor (a function with the same name as the contract) is executed once. -A constructor is optional. Only one constructor is allowed, and this means +A ``constructor`` is optional. Only one constructor is allowed, and this means overloading is not supported. From ``web3.js``, i.e. the JavaScript From 064918c94acba4ca261bd38228252d0b2cf8a6f3 Mon Sep 17 00:00:00 2001 From: James Ray Date: Fri, 28 Jul 2017 15:31:03 +1000 Subject: [PATCH 014/163] Constructor is not a solidity keyword Removed backticks --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 4e33c5f32dfd..3714667f9dd0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,7 +20,7 @@ Contracts can be created "from outside" or from Solidity contracts. When a contract is created, its constructor (a function with the same name as the contract) is executed once. -A ``constructor`` is optional. Only one constructor is allowed, and this means +A constructor is optional. Only one constructor is allowed, and this means overloading is not supported. From ``web3.js``, i.e. the JavaScript From 0c63f271a2d8c85823571e0c461cd213bebe44a5 Mon Sep 17 00:00:00 2001 From: James Ray Date: Sat, 29 Jul 2017 12:05:49 +1000 Subject: [PATCH 015/163] Updated getter-functions ref --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 6ad56ff357c7..cf5e46be4049 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -133,7 +133,7 @@ too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you added to the mapping or use it in a context where this is not needed, -like this one. The :ref:`getter function <_getter_functions>` created by the ``public`` keyword +like this one. The :ref:`getter-functions` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: From 5f9c02c6b6e78ec80a5b8c2e6a467bb89b9a9454 Mon Sep 17 00:00:00 2001 From: Joshua Hannan Date: Sat, 29 Jul 2017 18:15:54 -0500 Subject: [PATCH 016/163] added while loop description --- docs/assembly.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index 4e665b7ec640..a56e9cc11783 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -545,6 +545,21 @@ The following example computes the sum of an area in memory. } } +With the for loop header, it is also possible to declare it so it behaves +like a while loop. Simply leave the initialization and post-iteration +parts empty. + +.. code:: + + { + let x := 0 + let i := 0 + for { } lt(i, 0x100) { } { // while(i < 0x100) + x := add(x, mload(i)) + i := add(i, 0x20) + } + } + Functions --------- From a73fb4d52957317cd341ad1a126ce3a499889d7b Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 31 Jul 2017 11:44:35 +0200 Subject: [PATCH 017/163] Wording. --- docs/assembly.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index a56e9cc11783..a7cddb1878d5 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -545,9 +545,8 @@ The following example computes the sum of an area in memory. } } -With the for loop header, it is also possible to declare it so it behaves -like a while loop. Simply leave the initialization and post-iteration -parts empty. +For loops can also be written so that they behave like while loops: +Simply leave the initialization and post-iteration parts empty. .. code:: From 8a19b2f24eeeae37a7d8e4ab1b764d046f55411b Mon Sep 17 00:00:00 2001 From: James Ray Date: Mon, 31 Jul 2017 20:26:26 +1000 Subject: [PATCH 018/163] :ref:`getter function` --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index cf5e46be4049..1f1373cfb1ca 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -133,7 +133,7 @@ too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you added to the mapping or use it in a context where this is not needed, -like this one. The :ref:`getter-functions` created by the ``public`` keyword +like this one. The :ref:`getter function` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: From 5678a08d589b5ab931813463c49cbd7aae99e245 Mon Sep 17 00:00:00 2001 From: James Ray Date: Mon, 31 Jul 2017 20:32:34 +1000 Subject: [PATCH 019/163] :ref:`getter function` --- docs/frequently-asked-questions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index e7ccb3db3822..47c87c5ac25e 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? =========================================================================================================================== -The automatic :ref:`getter-functions` for a public state variable of array type only returns +The automatic :ref:`getter function` for a public state variable of array type only returns individual elements. If you want to return the complete array, you have to manually write a function to do that. From 0da46f259f357c09303363147bb8b2cc738843f6 Mon Sep 17 00:00:00 2001 From: James Ray Date: Mon, 31 Jul 2017 20:35:29 +1000 Subject: [PATCH 020/163] :ref:`getter function` --- docs/miscellaneous.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 493e701bdb85..6e5c1c9e0ace 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -491,7 +491,7 @@ Function Visibility Specifiers return true; } -- ``public``: visible externally and internally (creates a :ref:`getter-functions` for storage/state variables) +- ``public``: visible externally and internally (creates a :ref:`getter function` for storage/state variables) - ``private``: only visible in the current contract - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``internal``: only visible internally From df417934666329090b54d325b31dc2a7f1bd6d1c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 9 Aug 2017 11:40:46 +0200 Subject: [PATCH 021/163] Set version to 0.4.16. --- CMakeLists.txt | 2 +- Changelog.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60a8158de07b..a11c56ee6bdf 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.15") +set(PROJECT_VERSION "0.4.16") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/Changelog.md b/Changelog.md index 99466fd19bf0..a61ac0af3c9c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +### 0.4.16 (unreleased) + +Features: + +Bugfixes: + ### 0.4.15 (2017-08-08) Features: From 3e2f0b589d384ff264bbbb05bb5cbe003a63f936 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 12:44:53 +0100 Subject: [PATCH 022/163] Remove some global symbols --- solc/jsonCompiler.cpp | 5 +++++ solc/main.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index de797b3c8f43..ab928ac0669f 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -38,6 +38,9 @@ extern "C" { typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); } +namespace +{ + ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr) { ReadFile::Callback readCallback; @@ -260,6 +263,8 @@ string compileStandardInternal(string const& _input, CStyleReadFileCallback _rea return compiler.compile(_input); } +} + static string s_outputBuffer; extern "C" diff --git a/solc/main.cpp b/solc/main.cpp index c61da6e9cef3..6d5595422997 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -40,7 +40,7 @@ specified default locale if it is valid, and if not then it will modify the environment the process is running in to use a sensible default. This also means that users do not need to install language packs for their OS. */ -void setDefaultOrCLocale() +static void setDefaultOrCLocale() { #if __unix__ if (!std::setlocale(LC_ALL, "")) From 79137e4703de2ee00d472e863eafe877fb1528e5 Mon Sep 17 00:00:00 2001 From: vladislav-ankudinov Date: Wed, 9 Aug 2017 14:46:51 +0300 Subject: [PATCH 023/163] fix typo 'onte' -> 'onto' --- docs/assembly.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 4e665b7ec640..2f5b88124f21 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -125,7 +125,7 @@ following list can be used as a reference of its opcodes. If an opcode takes arguments (always from the top of the stack), they are given in parentheses. 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 onte the stack. +special and all others push exactly one item onto the stack. 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``. From 3b41cd3c23d346c7b7367bd80fd6381bbb329587 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 12:50:53 +0100 Subject: [PATCH 024/163] Rename Token::Const to Token::Constant --- libsolidity/parsing/Parser.cpp | 4 ++-- libsolidity/parsing/Token.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b98991f392f1..bc004d14af1b 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -321,7 +321,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN while (true) { Token::Value token = m_scanner->currentToken(); - if (token == Token::Const) + if (token == Token::Constant) { if (result.isDeclaredConst) parserError(string("Multiple \"constant\" specifiers.")); @@ -522,7 +522,7 @@ ASTPointer Parser::parseVariableDeclaration( { if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; - else if (token == Token::Const) + else if (token == Token::Constant) isDeclaredConst = true; else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) { diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index d412b3f0fd5b..468cbcb71e1e 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -143,7 +143,7 @@ namespace solidity K(As, "as", 0) \ K(Assembly, "assembly", 0) \ K(Break, "break", 0) \ - K(Const, "constant", 0) \ + K(Constant, "constant", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Do, "do", 0) \ From a8ca623a0f852c738b8bb92b449894ecc73b44e1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 13:22:19 +0100 Subject: [PATCH 025/163] Add test for multiple visibilites on functions --- libsolidity/parsing/Parser.cpp | 2 +- test/libsolidity/SolidityParser.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index bc004d14af1b..d1f9c1faaca6 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -354,7 +354,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN { if (result.visibility != Declaration::Visibility::Default) { - parserError(string("Multiple visibility specifiers.")); + parserError(string("Visibility already specified.")); m_scanner->next(); } else diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 78edd4d1b344..d7ada518644f 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -898,7 +898,12 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) contract c { uint private internal a; })"; - CHECK_PARSE_ERROR(text, "Visibility already specified"); + CHECK_PARSE_ERROR(text, "Visibility already specified."); + text = R"( + contract c { + function f() private external {} + })"; + CHECK_PARSE_ERROR(text, "Visibility already specified."); } BOOST_AUTO_TEST_CASE(multiple_payable_specifiers) From efd45f64a542c722d750f79b483dd7428aba16ed Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 13:35:38 +0100 Subject: [PATCH 026/163] Show previous visibility specifier in parser error --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 30 +++++++++++++++++++++++++++-- libsolidity/parsing/Parser.h | 1 + test/libsolidity/SolidityParser.cpp | 4 ++-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index a61ac0af3c9c..0381d4346de5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.16 (unreleased) Features: + * Parser: Display previous visibility specifier in error if multiple are found. Bugfixes: diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d1f9c1faaca6..7e1d36dd1972 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -307,6 +307,24 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) return visibility; } +string Parser::visibilitySpecifierName(Declaration::Visibility _visibility) +{ + switch(_visibility) + { + case Declaration::Visibility::Public: + return "public"; + case Declaration::Visibility::Internal: + return "internal"; + case Declaration::Visibility::Private: + return "private"; + case Declaration::Visibility::External: + return "external"; + default: + solAssert(false, "Invalid visibility specifier."); + } + return string(); +} + Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { FunctionHeaderParserResult result; @@ -354,7 +372,11 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN { if (result.visibility != Declaration::Visibility::Default) { - parserError(string("Visibility already specified.")); + parserError(string( + "Visibility already specified as \"" + + visibilitySpecifierName(result.visibility) + + "\"." + )); m_scanner->next(); } else @@ -512,7 +534,11 @@ ASTPointer Parser::parseVariableDeclaration( { if (visibility != Declaration::Visibility::Default) { - parserError(string("Visibility already specified.")); + parserError(string( + "Visibility already specified as \"" + + visibilitySpecifierName(visibility) + + "\"." + )); m_scanner->next(); } else diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 19631c585a32..82ab91a64af4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -73,6 +73,7 @@ class Parser: public ParserBase ASTPointer parseContractDefinition(Token::Value _expectedKind); ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); + std::string visibilitySpecifierName(Declaration::Visibility _visibility); FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); ASTPointer parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer parseFunctionDefinition(ASTString const* _contractName); diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index d7ada518644f..2be824d6baeb 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -898,12 +898,12 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) contract c { uint private internal a; })"; - CHECK_PARSE_ERROR(text, "Visibility already specified."); + CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); text = R"( contract c { function f() private external {} })"; - CHECK_PARSE_ERROR(text, "Visibility already specified."); + CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); } BOOST_AUTO_TEST_CASE(multiple_payable_specifiers) From b210db8058851d19005f8ecd9d107a16e9c80e32 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 14:29:03 +0100 Subject: [PATCH 027/163] Make toString(visibility) a helper --- libsolidity/ast/AST.h | 18 ++++++++++++++++++ libsolidity/ast/ASTJsonConverter.cpp | 27 +++++---------------------- libsolidity/ast/ASTJsonConverter.h | 1 - libsolidity/parsing/Parser.cpp | 22 ++-------------------- 4 files changed, 25 insertions(+), 43 deletions(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 3e97286bc061..bdf20e819da5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -152,6 +152,24 @@ class Declaration: public ASTNode /// Visibility ordered from restricted to unrestricted. enum class Visibility { Default, Private, Internal, Public, External }; + static std::string visibilityToString(Declaration::Visibility _visibility) + { + switch(_visibility) + { + case Declaration::Visibility::Public: + return "public"; + case Declaration::Visibility::Internal: + return "internal"; + case Declaration::Visibility::Private: + return "private"; + case Declaration::Visibility::External: + return "external"; + default: + solAssert(false, "Invalid visibility specifier."); + } + return std::string(); + } + Declaration( SourceLocation const& _location, ASTPointer const& _name, diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index e4a602cba4af..abee55eedb99 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -285,7 +285,7 @@ bool ASTJsonConverter::visit(StructDefinition const& _node) { setJsonNode(_node, "StructDefinition", { make_pair("name", _node.name()), - make_pair("visibility", visibility(_node.visibility())), + make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("canonicalName", _node.annotation().canonicalName), make_pair("members", toJson(_node.members())), make_pair("scope", idOrNull(_node.scope())) @@ -325,7 +325,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair("name", _node.name()), make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), make_pair("payable", _node.isPayable()), - make_pair("visibility", visibility(_node.visibility())), + make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("isConstructor", _node.isConstructor()), make_pair("returnParameters", toJson(*_node.returnParameterList())), @@ -346,7 +346,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) make_pair("constant", _node.isConstant()), make_pair("stateVariable", _node.isStateVariable()), make_pair("storageLocation", location(_node.referenceLocation())), - make_pair("visibility", visibility(_node.visibility())), + make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue), make_pair("scope", idOrNull(_node.scope())), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) @@ -361,7 +361,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node) { setJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()), - make_pair("visibility", visibility(_node.visibility())), + make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("body", toJson(_node.body())) }); @@ -418,7 +418,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) { setJsonNode(_node, "FunctionTypeName", { make_pair("payable", _node.isPayable()), - make_pair("visibility", visibility(_node.visibility())), + make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), @@ -730,23 +730,6 @@ void ASTJsonConverter::endVisit(EventDefinition const&) m_inEvent = false; } -string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility) -{ - switch (_visibility) - { - case Declaration::Visibility::Private: - return "private"; - case Declaration::Visibility::Internal: - return "internal"; - case Declaration::Visibility::Public: - return "public"; - case Declaration::Visibility::External: - return "external"; - default: - solAssert(false, "Unknown declaration visibility."); - } -} - string ASTJsonConverter::location(VariableDeclaration::Location _location) { switch (_location) diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 27114c2a229c..70e260db8ddf 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -128,7 +128,6 @@ class ASTJsonConverter: public ASTConstVisitor return _node ? toJson(*_node) : Json::nullValue; } Json::Value inlineAssemblyIdentifierToJson(std::pair _info); - std::string visibility(Declaration::Visibility const& _visibility); std::string location(VariableDeclaration::Location _location); std::string contractKind(ContractDefinition::ContractKind _kind); std::string functionCallKind(FunctionCallKind _kind); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 7e1d36dd1972..a6d6e025ae07 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -307,24 +307,6 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) return visibility; } -string Parser::visibilitySpecifierName(Declaration::Visibility _visibility) -{ - switch(_visibility) - { - case Declaration::Visibility::Public: - return "public"; - case Declaration::Visibility::Internal: - return "internal"; - case Declaration::Visibility::Private: - return "private"; - case Declaration::Visibility::External: - return "external"; - default: - solAssert(false, "Invalid visibility specifier."); - } - return string(); -} - Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { FunctionHeaderParserResult result; @@ -374,7 +356,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN { parserError(string( "Visibility already specified as \"" + - visibilitySpecifierName(result.visibility) + + Declaration::visibilityToString(result.visibility) + "\"." )); m_scanner->next(); @@ -536,7 +518,7 @@ ASTPointer Parser::parseVariableDeclaration( { parserError(string( "Visibility already specified as \"" + - visibilitySpecifierName(visibility) + + Declaration::visibilityToString(visibility) + "\"." )); m_scanner->next(); From b38f31617e83fac70d9ca542cc79cc23a7d6a2ee Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 12:41:45 +0200 Subject: [PATCH 028/163] Add isDynamicallyEncoded member function to types. --- libsolidity/ast/Types.cpp | 10 ++++++++++ libsolidity/ast/Types.h | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 56fdd508f3f2..cf43d3fea152 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1408,6 +1408,11 @@ unsigned ArrayType::calldataEncodedSize(bool _padded) const return unsigned(size); } +bool ArrayType::isDynamicallyEncoded() const +{ + return isDynamicallySized() || baseType()->isDynamicallyEncoded(); +} + u256 ArrayType::storageSize() const { if (isDynamicallySized()) @@ -1710,6 +1715,11 @@ unsigned StructType::calldataEncodedSize(bool _padded) const return size; } +bool StructType::isDynamicallyEncoded() const +{ + solAssert(false, "Structs are not yet supported in the ABI."); +} + u256 StructType::memorySize() const { u256 size; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 5d2bdca06b77..8c9232c6f935 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -187,6 +187,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_thiscanLiveOutsideStorage(); } virtual unsigned sizeOnStack() const override; @@ -723,6 +727,7 @@ class StructType: public ReferenceType virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; + virtual bool isDynamicallyEncoded() const override; u256 memorySize() const; virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return true; } From fbcc5f4ee5bd629474047531f76beaa19c62972b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 9 Jul 2017 00:10:22 +0100 Subject: [PATCH 029/163] Support experimental feature pragma --- libsolidity/analysis/SyntaxChecker.cpp | 32 ++++++++++++++++++++++---- libsolidity/analysis/SyntaxChecker.h | 2 ++ libsolidity/ast/ASTAnnotations.h | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index bde0e616eb91..04c4e2d83b80 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -33,9 +33,10 @@ bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) return Error::containsOnlyWarnings(m_errorReporter.errors()); } -bool SyntaxChecker::visit(SourceUnit const&) +bool SyntaxChecker::visit(SourceUnit const& _sourceUnit) { m_versionPragmaFound = false; + m_sourceUnit = &_sourceUnit; return true; } @@ -57,15 +58,36 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) m_errorReporter.warning(_sourceUnit.location(), errorString); } + m_sourceUnit = nullptr; } bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); - if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity") - m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); - else + if (_pragma.tokens()[0] != Token::Identifier) + m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); + else if (_pragma.literals()[0] == "experimental") + { + solAssert(m_sourceUnit, ""); + vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + if (literals.size() == 0) + m_errorReporter.syntaxError( + _pragma.location(), + "At least one experimental feature or the wildcard symbol \"*\" is required." + ); + else + for (string const literal: literals) + { + if (literal.empty()) + m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); + else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) + m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); + else + m_sourceUnit->annotation().experimentalFeatures.insert(literal); + } + } + else if (_pragma.literals()[0] == "solidity") { vector tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); @@ -81,6 +103,8 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) ); m_versionPragmaFound = true; } + else + m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); return true; } diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index fb5cc6d7af35..fa34bab312eb 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -77,6 +77,8 @@ class SyntaxChecker: private ASTConstVisitor bool m_versionPragmaFound = false; int m_inLoopDepth = 0; + + SourceUnit const* m_sourceUnit = nullptr; }; } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index f757f03c0c35..083ecfa4840a 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -61,6 +61,8 @@ struct SourceUnitAnnotation: ASTAnnotation std::string path; /// The exported symbols (all global symbols). std::map> exportedSymbols; + /// Experimental feature pragmas. + std::set experimentalFeatures; }; struct ImportAnnotation: ASTAnnotation From e44da40835670e667eda0ca30467fa52328a9075 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 2 Aug 2017 20:05:35 +0100 Subject: [PATCH 030/163] Warn if using experimental pragma --- libsolidity/analysis/SyntaxChecker.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 04c4e2d83b80..db3e0a7ae4e7 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -77,6 +77,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) "At least one experimental feature or the wildcard symbol \"*\" is required." ); else + { for (string const literal: literals) { if (literal.empty()) @@ -84,8 +85,12 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else + { m_sourceUnit->annotation().experimentalFeatures.insert(literal); + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + } } + } } else if (_pragma.literals()[0] == "solidity") { From de9e758ef7c0a2f60d526bb94c834a4e3c2a737a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 2 Aug 2017 20:21:20 +0100 Subject: [PATCH 031/163] Reject unsupported experimental feature names --- libsolidity/analysis/SyntaxChecker.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index db3e0a7ae4e7..8c92f439ea4f 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -69,6 +69,9 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); else if (_pragma.literals()[0] == "experimental") { + /// TODO: fill this out + static const set validFeatures = set{}; + solAssert(m_sourceUnit, ""); vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); if (literals.size() == 0) @@ -82,6 +85,8 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { if (literal.empty()) m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); + else if (!validFeatures.count(literal)) + m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else From 690ed37fd4b90119ac69def3e308035d46af0c60 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 4 Aug 2017 23:28:28 +0100 Subject: [PATCH 032/163] Reject wildcard and multiple experimental pragmas --- libsolidity/analysis/SyntaxChecker.cpp | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 8c92f439ea4f..39f788c40d1b 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -77,23 +77,26 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) if (literals.size() == 0) m_errorReporter.syntaxError( _pragma.location(), - "At least one experimental feature or the wildcard symbol \"*\" is required." + "Experimental feature name is missing." + ); + else if (literals.size() > 1) + m_errorReporter.syntaxError( + _pragma.location(), + "Stray arguments." ); else { - for (string const literal: literals) + string const literal = literals[0]; + if (literal.empty()) + m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); + else if (!validFeatures.count(literal)) + m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); + else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) + m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); + else { - if (literal.empty()) - m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); - else if (!validFeatures.count(literal)) - m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); - else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) - m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); - else - { - m_sourceUnit->annotation().experimentalFeatures.insert(literal); - m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); - } + m_sourceUnit->annotation().experimentalFeatures.insert(literal); + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); } } } From 57c24511308f9cd1f981e92aacfb880825eb6ed9 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 8 Aug 2017 14:09:57 +0100 Subject: [PATCH 033/163] Introduce ExperimentalFeatures header --- libsolidity/analysis/SyntaxChecker.cpp | 10 +++----- libsolidity/ast/ASTAnnotations.h | 5 ++-- libsolidity/ast/ExperimentalFeatures.h | 35 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 libsolidity/ast/ExperimentalFeatures.h diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 39f788c40d1b..d2571cd35600 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -69,9 +70,6 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); else if (_pragma.literals()[0] == "experimental") { - /// TODO: fill this out - static const set validFeatures = set{}; - solAssert(m_sourceUnit, ""); vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); if (literals.size() == 0) @@ -89,13 +87,13 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) string const literal = literals[0]; if (literal.empty()) m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); - else if (!validFeatures.count(literal)) + else if (!ExperimentalFeatureNames.count(literal)) m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); - else if (m_sourceUnit->annotation().experimentalFeatures.count(literal)) + else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal))) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else { - m_sourceUnit->annotation().experimentalFeatures.insert(literal); + 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."); } } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 083ecfa4840a..fd9efb4d9f56 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include @@ -61,8 +62,8 @@ struct SourceUnitAnnotation: ASTAnnotation std::string path; /// The exported symbols (all global symbols). std::map> exportedSymbols; - /// Experimental feature pragmas. - std::set experimentalFeatures; + /// Experimental features. + std::set experimentalFeatures; }; struct ImportAnnotation: ASTAnnotation diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h new file mode 100644 index 000000000000..b0a0714266a7 --- /dev/null +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -0,0 +1,35 @@ +/* + 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 . +*/ +/** + * List of experimental features. + */ + +#pragma once + +#include + +namespace dev +{ +namespace solidity +{ + +enum class ExperimentalFeature {}; + +static const std::map ExperimentalFeatureNames = {}; + +} +} From 470950e75e6f3a8a37d2a8fe1c263149be01c402 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 8 Aug 2017 14:41:46 +0100 Subject: [PATCH 034/163] Add tests for experimental pragma --- .../SolidityNameAndTypeResolution.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2fbc6ac8ffc4..55ce5f7bdd36 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6558,6 +6558,43 @@ BOOST_AUTO_TEST_CASE(library_function_without_implementation) CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared."); } +BOOST_AUTO_TEST_CASE(experimental_pragma) +{ + char const* text = R"( + pragma experimental; + )"; + CHECK_ERROR(text, SyntaxError, "Experimental feature name is missing."); + text = R"( + pragma experimental 123; + )"; + CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); + text = R"( + pragma experimental unsupportedName; + )"; + CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); + text = R"( + pragma experimental "unsupportedName"; + )"; + CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); + text = R"( + pragma experimental ""; + )"; + CHECK_ERROR(text, SyntaxError, "Empty experimental feature name is invalid."); + text = R"( + pragma experimental unsupportedName unsupportedName; + )"; + CHECK_ERROR(text, SyntaxError, "Stray arguments."); +// text = R"( +// pragma experimental supportedName; +// )"; +// CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments."); +// text = R"( +// pragma experimental supportedName; +// pragma experimental supportedName; +// )"; +// CHECK_ERROR(text, SyntaxError, "Duplicate experimental feature name."); +} + BOOST_AUTO_TEST_SUITE_END() } From 95acbc7a9f03eecf837d87eb72f115c1b1650487 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 12:52:40 +0200 Subject: [PATCH 035/163] Some more ABI tests. --- test/boostTest.cpp | 1 + test/libsolidity/ABIEncoderTests.cpp | 259 ++++++++++++++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 94 ++++++++ 3 files changed, 354 insertions(+) create mode 100644 test/libsolidity/ABIEncoderTests.cpp diff --git a/test/boostTest.cpp b/test/boostTest.cpp index c2121940dd9a..d8c5b678a5c1 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -46,6 +46,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) if (dev::test::Options::get().disableIPC) { for (auto suite: { + "ABIEncoderTest", "SolidityAuctionRegistrar", "SolidityFixedFeeRegistrar", "SolidityWallet", diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp new file mode 100644 index 000000000000..44c673c5d112 --- /dev/null +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -0,0 +1,259 @@ +/* + 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 Solidity's ABI encoder. + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::placeholders; +using namespace dev::test; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +#define REQUIRE_LOG_DATA(DATA) do { \ + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); \ + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); \ + BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(DATA)); \ +} while (false) + +BOOST_FIXTURE_TEST_SUITE(ABIEncoderTest, SolidityExecutionFramework) + +BOOST_AUTO_TEST_CASE(value_types) +{ + char const* sourceCode = R"( + contract C { + event E(uint a, uint16 b, uint24 c, int24 d, bytes3 x, bool, C); + function f() { + bytes6 x = hex"1bababababa2"; + bool b; + assembly { b := 7 } + C c; + assembly { c := sub(0, 5) } + E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, u160(u256(-5)) + )); +} + +BOOST_AUTO_TEST_CASE(string_literal) +{ + char const* sourceCode = R"( + contract C { + event E(string, bytes20, string); + function f() { + E("abcdef", "abcde", "abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl"); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x60, string("abcde"), 0xa0, + 6, string("abcdef"), + 0x8b, string("abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl") + )); +} + + +BOOST_AUTO_TEST_CASE(enum_type_cleanup) +{ + char const* sourceCode = R"( + contract C { + enum E { A, B } + function f(uint x) returns (E en) { + assembly { en := x } + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", 0) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("f(uint256)", 1) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(uint256)", 2) == encodeArgs()); +} + +BOOST_AUTO_TEST_CASE(conversion) +{ + char const* sourceCode = R"( + contract C { + event E(bytes4, bytes4, uint16, uint8, int16, int8); + function f() { + bytes2 x; assembly { x := 0xf1f2f3f400000000000000000000000000000000000000000000000000000000 } + uint8 a; + uint16 b = 0x1ff; + int8 c; + int16 d; + assembly { a := sub(0, 1) c := 0x0101ff d := 0xff01 } + E(10, x, a, uint8(b), c, int8(d)); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + string(3, 0) + string("\x0a"), string("\xf1\xf2"), + 0xff, 0xff, u256(-1), u256(1) + )); +} + +BOOST_AUTO_TEST_CASE(storage_byte_array) +{ + char const* sourceCode = R"( + contract C { + bytes short; + bytes long; + event E(bytes s, bytes l); + function f() { + short = "123456789012345678901234567890a"; + long = "ffff123456789012345678901234567890afffffffff123456789012345678901234567890a"; + E(short, long); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x40, 0x80, + 31, string("123456789012345678901234567890a"), + 75, string("ffff123456789012345678901234567890afffffffff123456789012345678901234567890a") + )); +} + +BOOST_AUTO_TEST_CASE(storage_array) +{ + char const* sourceCode = R"( + contract C { + address[3] addr; + event E(address[3] a); + function f() { + assembly { + sstore(0, sub(0, 1)) + sstore(1, sub(0, 2)) + sstore(2, sub(0, 3)) + } + E(addr); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3))); +} + +BOOST_AUTO_TEST_CASE(storage_array_dyn) +{ + char const* sourceCode = R"( + contract C { + address[] addr; + event E(address[] a); + function f() { + addr.push(1); + addr.push(2); + addr.push(3); + E(addr); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(0x20, 3, u160(1), u160(2), u160(3))); +} + +BOOST_AUTO_TEST_CASE(storage_array_compact) +{ + char const* sourceCode = R"( + contract C { + int72[] x; + event E(int72[]); + function f() { + x.push(-1); + x.push(2); + x.push(-3); + x.push(4); + x.push(-5); + x.push(6); + x.push(-7); + x.push(8); + E(x); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x20, 8, u256(-1), 2, u256(-3), 4, u256(-5), 6, u256(-7), 8 + )); +} + +BOOST_AUTO_TEST_CASE(external_function) +{ + char const* sourceCode = R"( + contract C { + event E(function(uint) external returns (uint), function(uint) external returns (uint)); + function(uint) external returns (uint) g; + function f(uint) returns (uint) { + g = this.f; + E(this.f, g); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f(uint256)"); + string functionIdF = asString(m_contractAddress.ref()) + asString(FixedHash<4>(dev::keccak256("f(uint256)")).ref()); + REQUIRE_LOG_DATA(encodeArgs(functionIdF, functionIdF)); +} + +BOOST_AUTO_TEST_CASE(external_function_cleanup) +{ + char const* sourceCode = R"( + contract C { + event E(function(uint) external returns (uint), function(uint) external returns (uint)); + // This test relies on the fact that g is stored in slot zero. + function(uint) external returns (uint) g; + function f(uint) returns (uint) { + function(uint) external returns (uint)[1] memory h; + assembly { sstore(0, sub(0, 1)) mstore(h, sub(0, 1)) } + E(h[0], g); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f(uint256)"); + REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1)))); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d0c5285cfdf2..166c46601893 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3157,6 +3157,31 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } +BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) +{ + char const* sourceCode = R"( + contract ClientReceipt { + bytes x; + event Deposit(uint fixeda, bytes dynx, uint fixedb); + function deposit() { + x.length = 31; + x[0] = "A"; + x[1] = "B"; + x[2] = "C"; + x[30] = "Z"; + Deposit(10, x, 15); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("deposit()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 31, string("ABC") + string(27, 0) + "Z")); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_indexed_string) { char const* sourceCode = R"( @@ -4402,6 +4427,75 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) BOOST_CHECK(storageEmpty(m_contractAddress)); } +BOOST_AUTO_TEST_CASE(array_copy_storage_abi) +{ + // NOTE: This does not really test copying from storage to ABI directly, + // because it will always copy to memory first. + char const* sourceCode = R"( + contract c { + uint8[] x; + uint16[] y; + uint24[] z; + function test1() returns (uint8[]) { + for (uint i = 0; i < 101; ++i) + x.push(uint8(i)); + return x; + } + function test2() returns (uint16[]) { + for (uint i = 0; i < 101; ++i) + y.push(uint16(i)); + return y; + } + function test3() returns (uint24[]) { + for (uint i = 0; i < 101; ++i) + z.push(uint24(i)); + return z; + } + } + )"; + compileAndRun(sourceCode); + bytes valueSequence; + for (size_t i = 0; i < 101; ++i) + valueSequence += toBigEndian(u256(i)); + BOOST_CHECK(callContractFunction("test1()") == encodeArgs(0x20, 101) + valueSequence); + BOOST_CHECK(callContractFunction("test2()") == encodeArgs(0x20, 101) + valueSequence); + BOOST_CHECK(callContractFunction("test3()") == encodeArgs(0x20, 101) + valueSequence); +} + +BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed) +{ + // NOTE: This does not really test copying from storage to ABI directly, + // because it will always copy to memory first. + char const* sourceCode = R"( + contract c { + int16[] x; + function test() returns (int16[]) { + x.push(int16(-1)); + x.push(int16(-1)); + x.push(int16(8)); + x.push(int16(-16)); + x.push(int16(-2)); + x.push(int16(6)); + x.push(int16(8)); + x.push(int16(-1)); + return x; + } + } + )"; + compileAndRun(sourceCode); + bytes valueSequence; + BOOST_CHECK(callContractFunction("test()") == encodeArgs(0x20, 8, + u256(-1), + u256(-1), + u256(8), + u256(-16), + u256(-2), + u256(6), + u256(8), + u256(-1) + )); +} + BOOST_AUTO_TEST_CASE(array_push) { char const* sourceCode = R"( From 1b32cdcf21d284eec68c298b81d1c3b02a137ab3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 23:10:40 +0100 Subject: [PATCH 036/163] Check for payable when comparing function types --- libsolidity/ast/Types.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cf43d3fea152..892814bacf73 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2220,6 +2220,8 @@ string FunctionType::identifier() const } if (isConstant()) id += "_constant"; + if (isPayable()) + id += "_payable"; id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) id += "gas"; @@ -2238,9 +2240,13 @@ bool FunctionType::operator==(Type const& _other) const if (m_kind != other.m_kind) return false; + if (m_isConstant != other.isConstant()) return false; + if (m_isPayable != other.isPayable()) + return false; + if (m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) return false; @@ -2399,10 +2405,15 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const return FunctionTypePointer(); return make_shared( - paramTypes, retParamTypes, - m_parameterNames, m_returnParameterNames, - m_kind, m_arbitraryParameters, - m_declaration, m_isConstant, m_isPayable + paramTypes, + retParamTypes, + m_parameterNames, + m_returnParameterNames, + m_kind, + m_arbitraryParameters, + m_declaration, + m_isConstant, + m_isPayable ); } From ad7a63f8908241ead63be8687e5ec457cc56d1e8 Mon Sep 17 00:00:00 2001 From: gubatron Date: Wed, 9 Aug 2017 20:55:57 -0600 Subject: [PATCH 037/163] FunctionType operator== boolean refactor --- libsolidity/ast/Types.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 892814bacf73..8af603179c0c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2236,27 +2236,22 @@ bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - FunctionType const& other = dynamic_cast(_other); - - if (m_kind != other.m_kind) - return false; - - if (m_isConstant != other.isConstant()) - return false; - if (m_isPayable != other.isPayable()) + FunctionType const& other = dynamic_cast(_other); + if ( + m_kind != other.m_kind || + m_isConstant != other.isConstant() || + m_isPayable != other.isPayable() || + m_parameterTypes.size() != other.m_parameterTypes.size() || + m_returnParameterTypes.size() != other.m_returnParameterTypes.size() + ) return false; - if (m_parameterTypes.size() != other.m_parameterTypes.size() || - m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) - return false; auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; - - if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), - other.m_parameterTypes.cbegin(), typeCompare)) - return false; - if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), - other.m_returnParameterTypes.cbegin(), typeCompare)) + if ( + !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) || + !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare) + ) return false; //@todo this is ugly, but cannot be prevented right now if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet) From a323486a9bf40d60eb1a7914aaf38bb412c74ea0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 26 Jul 2017 15:01:17 +0100 Subject: [PATCH 038/163] Add pure assembly example for summing --- docs/assembly.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index 2f5b88124f21..5288089f27ae 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -96,6 +96,29 @@ you really know what you are doing. } } } + + // Same as above, but accomplish the entire code within inline assembly. + function sumPureAsm(uint[] _data) returns (uint o_sum) { + assembly { + // Load the length (first 32 bytes) + let len := mload(_data) + + // Skip over the length field. + _data := add(_data, 0x20) + + // Set up a bound. + let end := add(_data, len) + + // Iterate until the bound is not met. + for {} lt(_data, end) {} { + o_sum := add(o_sum, mload(_data)) + _data := add(_data, 0x20) + } + + // NOTE: after this point it is not safe to use _data in Solidity code + // because its offsets are in an invalid position + } + } } From 34503d98d7d95fc3a55a60be854809524c1f204e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 15:08:32 +0100 Subject: [PATCH 039/163] Move init/cond into the for loop --- docs/assembly.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 5288089f27ae..377a65304abc 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -106,13 +106,13 @@ you really know what you are doing. // Skip over the length field. _data := add(_data, 0x20) - // Set up a bound. - let end := add(_data, len) - // Iterate until the bound is not met. - for {} lt(_data, end) {} { + for + { let end := add(_data, len) } + lt(_data, end) + { _data := add(_data, 0x20) } + { o_sum := add(o_sum, mload(_data)) - _data := add(_data, 0x20) } // NOTE: after this point it is not safe to use _data in Solidity code From 9358001ba4074d94d9daa566d225e1f8b2305009 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 10 Aug 2017 17:47:35 +0100 Subject: [PATCH 040/163] Use temporary variable for sum example --- docs/assembly.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 377a65304abc..bdb708a91a9c 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -104,19 +104,21 @@ you really know what you are doing. let len := mload(_data) // Skip over the length field. - _data := add(_data, 0x20) + // + // Keep temporary variable so it can be incremented in place. + // + // NOTE: incrementing _data would result in an unusable + // _data variable after this assembly block + let data := add(_data, 0x20) // Iterate until the bound is not met. for - { let end := add(_data, len) } - lt(_data, end) - { _data := add(_data, 0x20) } + { let end := add(data, len) } + lt(data, end) + { data := add(data, 0x20) } { - o_sum := add(o_sum, mload(_data)) + o_sum := add(o_sum, mload(data)) } - - // NOTE: after this point it is not safe to use _data in Solidity code - // because its offsets are in an invalid position } } } From 3dcf089c3fd92e5f1c8323826fd5d28e470e1059 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 23:16:58 +0100 Subject: [PATCH 041/163] Simplify if/else statements in Types --- libsolidity/ast/Types.cpp | 45 +++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cf43d3fea152..abcfeb255330 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -525,19 +525,20 @@ bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const { - // "delete" is ok for all fixed types - if (_operator == Token::Delete) + switch(_operator) + { + case Token::Delete: + // "delete" is ok for all fixed types return make_shared(); - // for fixed, we allow +, -, ++ and -- - else if ( - _operator == Token::Add || - _operator == Token::Sub || - _operator == Token::Inc || - _operator == Token::Dec - ) + case Token::Add: + case Token::Sub: + case Token::Inc: + case Token::Dec: + // for fixed, we allow +, -, ++ and -- return shared_from_this(); - else + default: return TypePointer(); + } } bool FixedPointType::operator==(Type const& _other) const @@ -2354,14 +2355,26 @@ unsigned FunctionType::sizeOnStack() const } unsigned size = 0; - if (kind == Kind::External || kind == Kind::CallCode || kind == Kind::DelegateCall) + + switch(kind) + { + case Kind::External: + case Kind::CallCode: + case Kind::DelegateCall: size = 2; - else if (kind == Kind::BareCall || kind == Kind::BareCallCode || kind == Kind::BareDelegateCall) - size = 1; - else if (kind == Kind::Internal) - size = 1; - else if (kind == Kind::ArrayPush || kind == Kind::ByteArrayPush) + break; + case Kind::BareCall: + case Kind::BareCallCode: + case Kind::BareDelegateCall: + case Kind::Internal: + case Kind::ArrayPush: + case Kind::ByteArrayPush: size = 1; + break; + default: + break; + } + if (m_gasSet) size++; if (m_valueSet) From d5188fb6713f6e34d5769b97a1446dc586e71bab Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 15 Jun 2017 00:39:01 +0100 Subject: [PATCH 042/163] Introduce IndentedWriter --- libdevcore/IndentedWriter.cpp | 65 ++++++++++++++++++++++++++ libdevcore/IndentedWriter.h | 67 ++++++++++++++++++++++++++ test/libdevcore/IndentedWriter.cpp | 75 ++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 libdevcore/IndentedWriter.cpp create mode 100644 libdevcore/IndentedWriter.h create mode 100644 test/libdevcore/IndentedWriter.cpp diff --git a/libdevcore/IndentedWriter.cpp b/libdevcore/IndentedWriter.cpp new file mode 100644 index 000000000000..96aaf0fa49b2 --- /dev/null +++ b/libdevcore/IndentedWriter.cpp @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Indented text writer. + */ + +#include +#include + +using namespace std; +using namespace dev; + +string IndentedWriter::format() const +{ + string result; + for (auto const& line: m_lines) + result += string(line.indentation * 4, ' ') + line.contents + "\n"; + return result; +} + +void IndentedWriter::newLine() +{ + if (!m_lines.back().contents.empty()) + m_lines.push_back({ string(), m_lines.back().indentation }); +} + +void IndentedWriter::indent() +{ + newLine(); + m_lines.back().indentation++; +} + +void IndentedWriter::unindent() +{ + newLine(); + assertThrow(m_lines.back().indentation > 0, IndentedWriterError, "Negative indentation."); + m_lines.back().indentation--; +} + +void IndentedWriter::add(string const& _str) +{ + m_lines.back().contents += _str; +} + +void IndentedWriter::addLine(string const& _line) +{ + newLine(); + add(_line); + newLine(); +} diff --git a/libdevcore/IndentedWriter.h b/libdevcore/IndentedWriter.h new file mode 100644 index 000000000000..4ddd87ed3ba9 --- /dev/null +++ b/libdevcore/IndentedWriter.h @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Indented text writer. + */ + +#pragma once + +#include +#include + +#include + +namespace dev +{ + +DEV_SIMPLE_EXCEPTION(IndentedWriterError); + +class IndentedWriter +{ +public: + explicit IndentedWriter(): m_lines(std::vector{{std::string(), 0}}) {} + + // Returns the formatted output. + std::string format() const; + + // Go one indentation level in. + void indent(); + + // Go one indentation level out. + void unindent(); + + // Add text. + void add(std::string const& _str); + + // Add text with new line. + void addLine(std::string const& _line); + + // Add new line. + void newLine(); + +private: + struct Line + { + std::string contents; + unsigned indentation; + }; + + std::vector m_lines; +}; + +} diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp new file mode 100644 index 000000000000..a694aa1b9724 --- /dev/null +++ b/test/libdevcore/IndentedWriter.cpp @@ -0,0 +1,75 @@ +/* + 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 IndentedWriter. + */ + +#include + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(IndentedWriterTest) + +BOOST_AUTO_TEST_CASE(empty) +{ + IndentedWriter iw; + BOOST_CHECK_EQUAL(iw.format(), "\n"); +} + +BOOST_AUTO_TEST_CASE(new_lines) +{ + IndentedWriter iw; + iw.newLine(); + BOOST_CHECK_EQUAL(iw.format(), "\n"); +} + +BOOST_AUTO_TEST_CASE(text_without_newline) +{ + IndentedWriter iw; + iw.add("Hello World"); + BOOST_CHECK_EQUAL(iw.format(), "Hello World\n"); +} + +BOOST_AUTO_TEST_CASE(text_with_newline) +{ + IndentedWriter iw; + iw.addLine("Hello World"); + BOOST_CHECK_EQUAL(iw.format(), "Hello World\n\n"); +} + +BOOST_AUTO_TEST_CASE(indent) +{ + IndentedWriter iw; + iw.addLine("Hello"); + iw.indent(); + iw.addLine("World"); + iw.unindent(); + iw.addLine("and everyone else"); + BOOST_CHECK_EQUAL(iw.format(), "Hello\n World\nand everyone else\n\n"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} From 644c91fc2dedf399b2731b37a0795a18b6cee04e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 10 Aug 2017 17:09:44 +0100 Subject: [PATCH 043/163] Add __test experimental mode for testing --- libsolidity/ast/ExperimentalFeatures.h | 8 ++++++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 +++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index b0a0714266a7..2338b881a5fe 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -27,9 +27,13 @@ namespace dev namespace solidity { -enum class ExperimentalFeature {}; +enum class ExperimentalFeature { + Test +}; -static const std::map ExperimentalFeatureNames = {}; +static const std::map ExperimentalFeatureNames = { + { "__test", ExperimentalFeature::Test }, +}; } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 55ce5f7bdd36..74422db14017 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6584,15 +6584,15 @@ BOOST_AUTO_TEST_CASE(experimental_pragma) pragma experimental unsupportedName unsupportedName; )"; CHECK_ERROR(text, SyntaxError, "Stray arguments."); + text = R"( + pragma experimental __test; + )"; + CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments."); // text = R"( -// pragma experimental supportedName; -// )"; -// CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments."); -// text = R"( -// pragma experimental supportedName; -// pragma experimental supportedName; +// pragma experimental __test; +// pragma experimental __test; // )"; -// CHECK_ERROR(text, SyntaxError, "Duplicate experimental feature name."); +// CHECK_ERROR_ALLOW_MULTI(text, SyntaxError, "Duplicate experimental feature name."); } BOOST_AUTO_TEST_SUITE_END() From 53a497b4d85c0748243c899f0a860518eb54bddc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 10 Aug 2017 17:11:00 +0100 Subject: [PATCH 044/163] Add analysis-only experimental features --- libsolidity/ast/ExperimentalFeatures.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 2338b881a5fe..04b2630023ec 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -27,12 +27,19 @@ namespace dev namespace solidity { -enum class ExperimentalFeature { - Test +enum class ExperimentalFeature +{ + Test, + TestOnlyAnalysis +}; + +static const std::map ExperimentalFeatureOnlyAnalysis = { + { ExperimentalFeature::TestOnlyAnalysis, true }, }; static const std::map ExperimentalFeatureNames = { { "__test", ExperimentalFeature::Test }, + { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, }; } From 4d82d4f57acf11745bb2e1610c5777128f68bee4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 8 Aug 2017 21:32:57 +0100 Subject: [PATCH 045/163] Store experimental flag in metadata CBOR --- Changelog.md | 2 ++ libsolidity/interface/CompilerStack.cpp | 6 +++++ test/libsolidity/Metadata.cpp | 29 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/Changelog.md b/Changelog.md index 0381d4346de5..3f3d2e9872c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,8 @@ Features: * Parser: Display previous visibility specifier in error if multiple are found. + * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. + * Metadata: Store experimental flag in metadata CBOR. Bugfixes: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7c66a84302b3..bc52ff7acf2d 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -653,6 +653,12 @@ void CompilerStack::compileContract( // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + dev::swarmHash(metadata).asBytes(); + if (!_contract.sourceUnit().annotation().experimentalFeatures.empty()) + cborEncodedMetadata = + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true} + bytes{0xa2, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + + dev::swarmHash(metadata).asBytes() + + bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large"); // 16-bit big endian length cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2); diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 0d3cadddf53e..77679a3202e4 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -58,6 +58,35 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2)); } +BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) +{ + // Check that the metadata stamp is at the end of the runtime bytecode. + char const* sourceCode = R"( + pragma solidity >=0.0; + pragma experimental __test; + contract test { + function g(function(uint) external returns (uint) x) {} + } + )"; + CompilerStack compilerStack; + compilerStack.addSource("", std::string(sourceCode)); + compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); + ETH_TEST_REQUIRE_NO_THROW(compilerStack.compile(), "Compiling contract failed"); + bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; + std::string const& metadata = compilerStack.metadata("test"); + BOOST_CHECK(dev::test::isValidMetadata(metadata)); + bytes hash = dev::swarmHash(metadata).asBytes(); + BOOST_REQUIRE(hash.size() == 32); + BOOST_REQUIRE(bytecode.size() >= 2); + size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]); + BOOST_REQUIRE(metadataCBORSize < bytecode.size() - 2); + bytes expectation = + bytes{0xa2, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + + hash + + bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; + BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2)); +} + BOOST_AUTO_TEST_CASE(metadata_relevant_sources) { CompilerStack compilerStack; From 2d1bab0de8d6e2637b3827fd3c988ae538b1d9ef Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 10 Aug 2017 17:16:55 +0100 Subject: [PATCH 046/163] Output experimental flag in metadata only for risky features --- libsolidity/interface/CompilerStack.cpp | 36 ++++++++++++++++++------- test/libsolidity/Metadata.cpp | 1 + 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index bc52ff7acf2d..70bebfa53e2c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -632,6 +632,17 @@ string CompilerStack::absolutePath(string const& _path, string const& _reference return result.generic_string(); } +namespace +{ +bool onlySafeExperimentalFeaturesActivated(set const& features) +{ + for (auto const feature: features) + if (!ExperimentalFeatureOnlyAnalysis.count(feature)) + return false; + return true; +} +} + void CompilerStack::compileContract( ContractDefinition const& _contract, map& _compiledContracts @@ -649,16 +660,23 @@ void CompilerStack::compileContract( shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); string metadata = createMetadata(compiledContract); - bytes cborEncodedMetadata = - // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} - bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + - dev::swarmHash(metadata).asBytes(); - if (!_contract.sourceUnit().annotation().experimentalFeatures.empty()) + bytes cborEncodedHash = + // CBOR-encoding of the key "bzzr0" + bytes{0x65, 'b', 'z', 'z', 'r', '0'}+ + // CBOR-encoding of the hash + bytes{0x58, 0x20} + dev::swarmHash(metadata).asBytes(); + bytes cborEncodedMetadata; + if (onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures)) + cborEncodedMetadata = + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} + bytes{0xa1} + + cborEncodedHash; + else cborEncodedMetadata = - // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true} - bytes{0xa2, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + - dev::swarmHash(metadata).asBytes() + - bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true} + bytes{0xa2} + + cborEncodedHash + + bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large"); // 16-bit big endian length cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2); diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 77679a3202e4..c46e31605126 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -38,6 +38,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) // Check that the metadata stamp is at the end of the runtime bytecode. char const* sourceCode = R"( pragma solidity >=0.0; + pragma experimental __testOnlyAnalysis; contract test { function g(function(uint) external returns (uint) x) {} } From b5aeae008461bcc7fdad43034bace7484384c226 Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Sun, 30 Jul 2017 09:19:41 +0800 Subject: [PATCH 047/163] Amend expected test messages for SolidityNameAndTypeResolution --- .../SolidityNameAndTypeResolution.cpp | 336 +++++++++--------- 1 file changed, 168 insertions(+), 168 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 55ce5f7bdd36..a0c9ed50999e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) uint128 variable; } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(double_function_declaration) @@ -260,7 +260,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) function fun() { } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); } BOOST_AUTO_TEST_CASE(double_variable_declaration) @@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(reference_to_later_declaration) @@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Recursive struct definition."); } BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) @@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Recursive struct definition."); } BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) @@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) function f() returns (bool r1, bool r2) { return 1 >= 2; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration."); } BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) @@ -416,7 +416,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) function f() returns (uint256 r) { return 1 >= 2; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Return argument type bool is not implicitly convertible to expected type (type of first return variable) uint256."); } BOOST_AUTO_TEST_CASE(type_checking_function_call) @@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid) function f() { int32(2) == uint64(2); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Operator == not compatible with types int32 and uint64"); } BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) @@ -491,7 +491,7 @@ BOOST_AUTO_TEST_CASE(balance_invalid) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(assignment_to_mapping) @@ -508,7 +508,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_mapping) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Mappings cannot be assigned to."); } BOOST_AUTO_TEST_CASE(assignment_to_struct) @@ -535,7 +535,7 @@ BOOST_AUTO_TEST_CASE(returns_in_constructor) function test() returns (uint a) { } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Non-empty \"returns\" directive for constructor."); } BOOST_AUTO_TEST_CASE(forward_function_reference) @@ -674,7 +674,7 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) function foo() { b = new base(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Trying to create an instance of an abstract contract."); } BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) @@ -685,7 +685,7 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) contract derived is base { function foo() {} } contract wrong is derived { function foo(); } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Redeclaring an already implemented function as abstract"); } BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) @@ -817,7 +817,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) function g (C c) external {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested."); } BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) @@ -851,7 +851,7 @@ BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested."); } BOOST_AUTO_TEST_CASE(hash_collision_in_interface) @@ -862,7 +862,7 @@ BOOST_AUTO_TEST_CASE(hash_collision_in_interface) function tgeo() { } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Function signature hash collision for tgeo()"); } BOOST_AUTO_TEST_CASE(inheritance_basic) @@ -896,7 +896,7 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) contract A is B { } contract B is A { } )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, ""); + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Definition of base has to precede definition of derived contract"); } BOOST_AUTO_TEST_CASE(legal_override_direct) @@ -924,7 +924,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility) contract B { function f() internal {} } contract C is B { function f() public {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Override changes extended function signature."); } BOOST_AUTO_TEST_CASE(illegal_override_constness) @@ -933,7 +933,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_constness) contract B { function f() constant {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Override changes extended function signature."); } BOOST_AUTO_TEST_CASE(complex_inheritance) @@ -1003,7 +1003,7 @@ BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) function f() { B b = A(1); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type contract A is not implicitly convertible to expected type contract B."); } BOOST_AUTO_TEST_CASE(super_excludes_current_contract) @@ -1020,7 +1020,7 @@ BOOST_AUTO_TEST_CASE(super_excludes_current_contract) } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract super B"); } BOOST_AUTO_TEST_CASE(function_modifier_invocation) @@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) modifier mod1(uint a) { if (a > 0) _; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested."); } BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) @@ -1106,7 +1106,7 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override) contract A { modifier mod(uint a) { _; } } contract B is A { modifier mod(uint8 a) { _; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Override changes modifier signature."); } BOOST_AUTO_TEST_CASE(modifier_overrides_function) @@ -1117,7 +1117,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) )"; // Error: Identifier already declared. // Error: Override changes modifier to function. - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared"); + CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(function_overrides_modifier) @@ -1128,7 +1128,7 @@ BOOST_AUTO_TEST_CASE(function_overrides_modifier) )"; // Error: Identifier already declared. // Error: Override changes function to modifier. - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(modifier_returns_value) @@ -1194,7 +1194,7 @@ BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) function foo() {} } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(private_state_variable) @@ -1229,7 +1229,7 @@ BOOST_AUTO_TEST_CASE(missing_state_variable) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"stateVar\" not found or not visible after argument-dependent lookup in type(contract Scope)"); } @@ -1255,7 +1255,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only) Data public data; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Internal type is not allowed for public state variables."); } BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) @@ -1284,7 +1284,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1) function foo() returns (uint256) { return Parent2.m_aMember1; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"m_aMember1\" not found or not visible after argument-dependent lookup in type(contract Parent2)"); } BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) @@ -1301,7 +1301,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) uint256 public m_aMember3; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"m_aMember2\" not found or not visible after argument-dependent lookup in type(contract Child)"); } BOOST_AUTO_TEST_CASE(fallback_function) @@ -1323,7 +1323,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) function(uint a) { x = 2; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Fallback function cannot take parameters."); } BOOST_AUTO_TEST_CASE(fallback_function_in_library) @@ -1333,7 +1333,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_in_library) function() {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Libraries cannot have fallback functions."); } BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters) @@ -1343,7 +1343,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters) function() returns (uint) { } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Fallback function cannot return values."); } BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier) @@ -1354,7 +1354,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier) function() constant { x = 2; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Fallback function cannot be declared constant."); } BOOST_AUTO_TEST_CASE(fallback_function_twice) @@ -1366,7 +1366,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_twice) function() { x = 3; } } )"; - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Function with same name and arguments defined twice."); } BOOST_AUTO_TEST_CASE(fallback_function_inheritance) @@ -1401,7 +1401,7 @@ BOOST_AUTO_TEST_CASE(event_too_many_indexed) event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d); } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "More than 3 indexed arguments for event."); } BOOST_AUTO_TEST_CASE(anonymous_event_four_indexed) @@ -1421,7 +1421,7 @@ BOOST_AUTO_TEST_CASE(anonymous_event_too_many_indexed) event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "More than 4 indexed arguments for anonymous event."); } BOOST_AUTO_TEST_CASE(events_with_same_name) @@ -1540,7 +1540,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_function) function g() { c(0).f(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract c"); } BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) @@ -1553,7 +1553,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) function g() { c(0).a(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"a\" not found or not visible after argument-dependent lookup in contract c"); } BOOST_AUTO_TEST_CASE(access_to_internal_state_variable) @@ -1581,7 +1581,7 @@ BOOST_AUTO_TEST_CASE(error_count_in_named_args) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 1 arguments given but expected 2."); } BOOST_AUTO_TEST_CASE(empty_in_named_args) @@ -1596,7 +1596,7 @@ BOOST_AUTO_TEST_CASE(empty_in_named_args) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 0 arguments given but expected 2."); } BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) @@ -1611,7 +1611,7 @@ BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Duplicate named argument."); } BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) @@ -1626,7 +1626,7 @@ BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Named argument does not match function declaration."); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter) @@ -1680,7 +1680,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration."); } BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) @@ -1690,7 +1690,7 @@ BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) function f() { var (x) = f(); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Not enough components (0) in value to assign all variables (1)."); } BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) @@ -1713,7 +1713,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) uint256 a; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type int_const 115792089237316195423570985008687907853269984665640564039458000000000000000000 is not implicitly convertible to expected type uint256."); } BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) @@ -1723,7 +1723,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) function f() returns(uint d) { return 2 ** 10000000000; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Operator ** not compatible with types int_const 2 and int_const 10000000000"); } BOOST_AUTO_TEST_CASE(exp_warn_literal_base) @@ -1837,7 +1837,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_member_access) ActionChoices choices; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"RunAroundWavingYourHands\" not found or not visible after argument-dependent lookup in type(enum test.ActionChoices)"); } BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access) @@ -1851,7 +1851,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access) ActionChoices choices; } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) @@ -1897,7 +1897,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_256) uint256 a; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint256."); } BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64) @@ -1911,7 +1911,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64) uint64 b; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint64."); } BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay) @@ -1925,7 +1925,7 @@ BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed from \"enum test.Paper\" to \"enum test.Ground\"."); } BOOST_AUTO_TEST_CASE(enum_duplicate_values) @@ -1935,7 +1935,7 @@ BOOST_AUTO_TEST_CASE(enum_duplicate_values) enum ActionChoices { GoLeft, GoRight, GoLeft, Sit } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } BOOST_AUTO_TEST_CASE(enum_name_resolution_under_current_contract_name) @@ -1965,7 +1965,7 @@ BOOST_AUTO_TEST_CASE(private_visibility) function g() { f(); } } )"; - CHECK_ERROR(sourceCode, DeclarationError, ""); + CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) @@ -1978,7 +1978,7 @@ BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) function g() { base.f(); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); } BOOST_AUTO_TEST_CASE(external_visibility) @@ -1989,7 +1989,7 @@ BOOST_AUTO_TEST_CASE(external_visibility) function g() { f(); } } )"; - CHECK_ERROR(sourceCode, DeclarationError, ""); + CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(external_base_visibility) @@ -2002,7 +2002,7 @@ BOOST_AUTO_TEST_CASE(external_base_visibility) function g() { base.f(); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); } BOOST_AUTO_TEST_CASE(external_argument_assign) @@ -2012,7 +2012,7 @@ BOOST_AUTO_TEST_CASE(external_argument_assign) function f(uint a) external { a = 1; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(external_argument_increment) @@ -2022,7 +2022,7 @@ BOOST_AUTO_TEST_CASE(external_argument_increment) function f(uint a) external { a++; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(external_argument_delete) @@ -2032,7 +2032,7 @@ BOOST_AUTO_TEST_CASE(external_argument_delete) function f(uint a) external { delete a; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type) @@ -2055,7 +2055,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) function f(uint a) { uint8[a] x; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } BOOST_AUTO_TEST_CASE(array_with_negative_length) @@ -2077,7 +2077,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types1) function f() { b = a; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type bytes storage ref is not implicitly convertible to expected type uint256[] storage ref."); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) @@ -2089,7 +2089,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) function f() { b = a; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type uint32[] storage ref is not implicitly convertible to expected type uint8[] storage ref."); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible) @@ -2125,7 +2125,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static) function f() { b = a; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type uint256[] storage ref is not implicitly convertible to expected type uint256[80] storage ref."); } BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int) @@ -2135,7 +2135,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int) uint8 a = 1000; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type int_const 1000 is not implicitly convertible to expected type uint8."); } BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) @@ -2145,7 +2145,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) uint a = "abc"; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type uint256."); } BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) @@ -2306,7 +2306,7 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) uint constant x = 56; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); } BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) @@ -2416,7 +2416,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_const_variable) uint constant y; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Uninitialized \"constant\" variable."); } BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) @@ -2428,7 +2428,7 @@ BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) function g() returns(uint) { return f(3, 5); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after argument-dependent lookup."); } BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) @@ -2441,7 +2441,7 @@ BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) function g() returns(uint) { return f(1); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "No unique declaration found after argument-dependent lookup."); } BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) @@ -2464,7 +2464,7 @@ BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) function g() returns(uint) { var x = f; return x(7); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after variable lookup."); } BOOST_AUTO_TEST_CASE(external_types_clash) @@ -2478,7 +2478,7 @@ BOOST_AUTO_TEST_CASE(external_types_clash) function f(uint8 a) { } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Function overload clash during conversion to external types for arguments."); } BOOST_AUTO_TEST_CASE(override_changes_return_types) @@ -2491,7 +2491,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types) function f(uint a) returns (uint8) { } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Override changes extended function signature."); } BOOST_AUTO_TEST_CASE(multiple_constructors) @@ -2502,7 +2502,7 @@ BOOST_AUTO_TEST_CASE(multiple_constructors) function test() {} } )"; - CHECK_ERROR(sourceCode, DeclarationError, ""); + CHECK_ERROR(sourceCode, DeclarationError, "More than one constructor defined"); } BOOST_AUTO_TEST_CASE(equal_overload) @@ -2513,7 +2513,7 @@ BOOST_AUTO_TEST_CASE(equal_overload) function test(uint a) external {} } )"; - CHECK_ERROR_ALLOW_MULTI(sourceCode, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(sourceCode, DeclarationError, "Function with same name and arguments defined twice."); } BOOST_AUTO_TEST_CASE(uninitialized_var) @@ -2523,7 +2523,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_var) function f() returns (uint) { var x; return 2; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Assignment necessary for type detection."); } BOOST_AUTO_TEST_CASE(string) @@ -2575,7 +2575,7 @@ BOOST_AUTO_TEST_CASE(string_index) function f() { var a = s[2]; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Index access for string is not possible."); } BOOST_AUTO_TEST_CASE(string_length) @@ -2586,7 +2586,7 @@ BOOST_AUTO_TEST_CASE(string_length) function f() { var a = s.length; } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Member \"length\" not found or not visible after argument-dependent lookup in string storage ref"); } BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) @@ -2596,7 +2596,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) int8 public i = -129; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type int_const -129 is not implicitly convertible to expected type int8."); } BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min) @@ -2616,7 +2616,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound) int8 public j = 128; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type int_const 128 is not implicitly convertible to expected type int8."); } BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max) @@ -2636,7 +2636,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned) uint8 public x = -1; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type int_const -1 is not implicitly convertible to expected type uint8."); } BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) @@ -2646,7 +2646,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) uint8 public x = 700; } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type int_const 700 is not implicitly convertible to expected type uint8."); } BOOST_AUTO_TEST_CASE(integer_boolean_operators) @@ -2654,15 +2654,15 @@ BOOST_AUTO_TEST_CASE(integer_boolean_operators) char const* sourceCode1 = R"( contract test { function() { uint x = 1; uint y = 2; x || y; } } )"; - CHECK_ERROR(sourceCode1, TypeError, ""); + CHECK_ERROR(sourceCode1, TypeError, "Operator || not compatible with types uint256 and uint256"); char const* sourceCode2 = R"( contract test { function() { uint x = 1; uint y = 2; x && y; } } )"; - CHECK_ERROR(sourceCode2, TypeError, ""); + CHECK_ERROR(sourceCode2, TypeError, "Operator && not compatible with types uint256 and uint256"); char const* sourceCode3 = R"( contract test { function() { uint x = 1; !x; } } )"; - CHECK_ERROR(sourceCode3, TypeError, ""); + CHECK_ERROR(sourceCode3, TypeError, "Unary operator ! cannot be applied to type uint256"); } BOOST_AUTO_TEST_CASE(exp_signed_variable) @@ -2670,15 +2670,15 @@ BOOST_AUTO_TEST_CASE(exp_signed_variable) char const* sourceCode1 = R"( contract test { function() { uint x = 3; int y = -4; x ** y; } } )"; - CHECK_ERROR(sourceCode1, TypeError, ""); + CHECK_ERROR(sourceCode1, TypeError, "Operator ** not compatible with types uint256 and int256"); char const* sourceCode2 = R"( contract test { function() { uint x = 3; int y = -4; y ** x; } } )"; - CHECK_ERROR(sourceCode2, TypeError, ""); + CHECK_ERROR(sourceCode2, TypeError, "Operator ** not compatible with types int256 and uint256"); char const* sourceCode3 = R"( contract test { function() { int x = -3; int y = -4; x ** y; } } )"; - CHECK_ERROR(sourceCode3, TypeError, ""); + CHECK_ERROR(sourceCode3, TypeError, "Operator ** not compatible with types int256 and int256"); } BOOST_AUTO_TEST_CASE(reference_compare_operators) @@ -2686,11 +2686,11 @@ BOOST_AUTO_TEST_CASE(reference_compare_operators) char const* sourceCode1 = R"( contract test { bytes a; bytes b; function() { a == b; } } )"; - CHECK_ERROR(sourceCode1, TypeError, ""); + CHECK_ERROR(sourceCode1, TypeError, "Operator == not compatible with types bytes storage ref and bytes storage ref"); char const* sourceCode2 = R"( contract test { struct s {uint a;} s x; s y; function() { x == y; } } )"; - CHECK_ERROR(sourceCode2, TypeError, ""); + CHECK_ERROR(sourceCode2, TypeError, "Operator == not compatible with types struct test.s storage ref and struct test.s storage ref"); } BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) @@ -2700,7 +2700,7 @@ BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) function f(uint[] memory a) external {} } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword)."); } BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) @@ -2710,7 +2710,7 @@ BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) function f(uint[] storage a) external {} } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword)."); } BOOST_AUTO_TEST_CASE(storage_location_local_variables) @@ -2737,7 +2737,7 @@ BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type mapping(uint256 => uint256)[] memory is only valid in storage."); } BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) @@ -2751,7 +2751,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type uint256[] memory is not implicitly convertible to expected type uint256[] storage pointer."); } BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) @@ -2768,7 +2768,7 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type uint8[] storage pointer is not implicitly convertible to expected type uint256[] storage pointer."); } BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable) @@ -2808,7 +2808,7 @@ BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Unary operator delete cannot be applied to type uint256[] storage pointer"); } BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) @@ -2835,7 +2835,7 @@ BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from uint256[] memory to uint256[] storage pointer requested."); } BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) @@ -2864,7 +2864,7 @@ BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Type uint8[] memory is not implicitly convertible to expected type uint256[] memory."); } BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) @@ -2879,7 +2879,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) @@ -2892,7 +2892,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(struct_constructor) @@ -2962,7 +2962,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"b\" is not available in struct Test.S memory outside of storage."); } BOOST_AUTO_TEST_CASE(string_bytes_conversion) @@ -2988,7 +2988,7 @@ BOOST_AUTO_TEST_CASE(inheriting_from_library) library Lib {} contract Test is Lib {} )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Libraries cannot be inherited from."); } BOOST_AUTO_TEST_CASE(inheriting_library) @@ -2997,7 +2997,7 @@ BOOST_AUTO_TEST_CASE(inheriting_library) contract Test {} library Lib is Test {} )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Library is not allowed to inherit."); } BOOST_AUTO_TEST_CASE(library_having_variables) @@ -3005,7 +3005,7 @@ BOOST_AUTO_TEST_CASE(library_having_variables) char const* text = R"( library Lib { uint x; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Library cannot have non-constant state variables"); } BOOST_AUTO_TEST_CASE(valid_library) @@ -3038,7 +3038,7 @@ BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) function f() { var x = new Test(); } } )"; - CHECK_ERROR(sourceCode, TypeError, ""); + CHECK_ERROR(sourceCode, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); } BOOST_AUTO_TEST_CASE(array_out_of_bound_access) @@ -3052,7 +3052,7 @@ BOOST_AUTO_TEST_CASE(array_out_of_bound_access) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Out of bounds array access."); } BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer) @@ -3062,7 +3062,7 @@ BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer) function f() { string x = "abc"; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); } BOOST_AUTO_TEST_CASE(non_initialized_references) @@ -3092,7 +3092,7 @@ BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) function f() { keccak256(2**500); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid rational number (too large or division by zero)."); } BOOST_AUTO_TEST_CASE(cyclic_binary_dependency) @@ -3102,7 +3102,7 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency) contract B { function f() { new C(); } } contract C { function f() { new A(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); } BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance) @@ -3112,7 +3112,7 @@ BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance) contract B { function f() { new C(); } } contract C { function f() { new A(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Definition of base has to precede definition of derived contract"); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail) @@ -3152,7 +3152,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_1) function f() { var (a, b, ) = one(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2) { @@ -3162,7 +3162,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2) function f() { var (a, , ) = one(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3) @@ -3173,7 +3173,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3) function f() { var (, , a) = one(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4) @@ -3184,7 +3184,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4) function f() { var (, a, b) = one(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); } BOOST_AUTO_TEST_CASE(tuples) @@ -3212,7 +3212,7 @@ BOOST_AUTO_TEST_CASE(tuples_empty_components) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Tuple component cannot be empty."); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5) @@ -3223,7 +3223,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5) function f() { var (,) = one(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Wildcard both at beginning and end of variable declaration list is only allowed if the number of components is equal."); } BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6) @@ -3234,7 +3234,7 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6) function f() { var (a, b, c) = two(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Not enough components (2) in value to assign all variables (3)"); } BOOST_AUTO_TEST_CASE(tuple_assignment_from_void_function) @@ -3302,7 +3302,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_library) using D for uint; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Library name expected."); } BOOST_AUTO_TEST_CASE(using_for_function_exists) @@ -3393,7 +3393,7 @@ BOOST_AUTO_TEST_CASE(using_for_mismatch) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256"); } BOOST_AUTO_TEST_CASE(using_for_not_used) @@ -3409,7 +3409,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_used) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint16"); } BOOST_AUTO_TEST_CASE(library_memory_struct) @@ -3420,7 +3420,7 @@ BOOST_AUTO_TEST_CASE(library_memory_struct) function f() returns (S ) {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) @@ -3435,7 +3435,7 @@ BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256"); } BOOST_AUTO_TEST_CASE(bound_function_in_var) @@ -3482,7 +3482,7 @@ BOOST_AUTO_TEST_CASE(mapping_in_memory_array) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Type cannot live outside storage."); } BOOST_AUTO_TEST_CASE(new_for_non_array) @@ -3494,7 +3494,7 @@ BOOST_AUTO_TEST_CASE(new_for_non_array) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Contract or array type expected."); } BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) @@ -3506,7 +3506,7 @@ BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Wrong argument count for function call: 0 arguments given but expected 1."); } BOOST_AUTO_TEST_CASE(function_overload_array_type) @@ -3626,7 +3626,7 @@ BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Unable to deduce common type for array elements."); } BOOST_AUTO_TEST_CASE(dynamic_inline_array) @@ -3651,7 +3651,7 @@ BOOST_AUTO_TEST_CASE(lvalues_as_inline_array) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Inline array type cannot be declared as LValue."); } BOOST_AUTO_TEST_CASE(break_not_in_loop) @@ -3664,7 +3664,7 @@ BOOST_AUTO_TEST_CASE(break_not_in_loop) } } )"; - CHECK_ERROR(text, SyntaxError, ""); + CHECK_ERROR(text, SyntaxError, "\"break\" has to be in a \"for\" or \"while\" loop."); } BOOST_AUTO_TEST_CASE(continue_not_in_loop) @@ -3677,7 +3677,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop) } } )"; - CHECK_ERROR(text, SyntaxError, ""); + CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop."); } BOOST_AUTO_TEST_CASE(continue_not_in_loop_2) @@ -3692,7 +3692,7 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop_2) } } )"; - CHECK_ERROR(text, SyntaxError, ""); + CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop."); } BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression) @@ -3704,7 +3704,7 @@ BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "True expression's type bool doesn't match false expression's type uint8."); } BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) @@ -3718,7 +3718,7 @@ BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) } } )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, ""); + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Conditional expression as left value is not supported yet."); } BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) @@ -3738,7 +3738,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "True expression's type struct C.s1 memory doesn't match false expression's type struct C.s2 memory."); } BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type) @@ -3753,7 +3753,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "True expression's type function (bool) doesn't match false expression's type function ()."); } BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum) @@ -3771,7 +3771,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "True expression's type enum C.small doesn't match false expression's type enum C.big."); } BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping) @@ -3786,7 +3786,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "True expression's type mapping(uint8 => uint8) doesn't match false expression's type mapping(uint32 => uint8)."); } BOOST_AUTO_TEST_CASE(conditional_with_all_types) @@ -3893,7 +3893,7 @@ BOOST_AUTO_TEST_CASE(constructor_call_invalid_arg_count) } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Wrong argument count for modifier invocation: 1 arguments given but expected 0."); } BOOST_AUTO_TEST_CASE(index_access_for_bytes) @@ -4683,7 +4683,7 @@ BOOST_AUTO_TEST_CASE(modifier_without_underscore) modifier m() {} } )"; - CHECK_ERROR(text, SyntaxError, ""); + CHECK_ERROR(text, SyntaxError, "Modifier body does not contain '_'."); } BOOST_AUTO_TEST_CASE(payable_in_library) @@ -4693,7 +4693,7 @@ BOOST_AUTO_TEST_CASE(payable_in_library) function f() payable {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Library functions cannot be payable."); } BOOST_AUTO_TEST_CASE(payable_external) @@ -4713,7 +4713,7 @@ BOOST_AUTO_TEST_CASE(payable_internal) function f() payable internal {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal functions cannot be payable."); } BOOST_AUTO_TEST_CASE(payable_private) @@ -4723,7 +4723,7 @@ BOOST_AUTO_TEST_CASE(payable_private) function f() payable private {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal functions cannot be payable."); } BOOST_AUTO_TEST_CASE(illegal_override_payable) @@ -4732,7 +4732,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable) contract B { function f() payable {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Override changes extended function signature."); } BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) @@ -4741,7 +4741,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) contract B { function f() {} } contract C is B { function f() payable {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Override changes extended function signature."); } BOOST_AUTO_TEST_CASE(function_variable_mixin) @@ -4759,7 +4759,7 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin) function checkOk() returns (bool) { return ok(); } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } @@ -4768,7 +4768,7 @@ BOOST_AUTO_TEST_CASE(payable_constant_conflict) char const* text = R"( contract C { function f() payable constant {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Functions cannot be constant and payable at the same time."); } BOOST_AUTO_TEST_CASE(calling_payable) @@ -4792,7 +4792,7 @@ BOOST_AUTO_TEST_CASE(calling_nonpayable) function f() { (new receiver()).nopay.value(10)(); } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () external - did you forget the \"payable\" modifier?"); } BOOST_AUTO_TEST_CASE(non_payable_constructor) @@ -4808,7 +4808,7 @@ BOOST_AUTO_TEST_CASE(non_payable_constructor) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () returns (contract C) - did you forget the \"payable\" modifier?"); } BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma) @@ -4835,7 +4835,7 @@ BOOST_AUTO_TEST_CASE(constant_constructor) function test() constant {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Constructor cannot be defined as constant."); } BOOST_AUTO_TEST_CASE(external_constructor) @@ -4845,7 +4845,7 @@ BOOST_AUTO_TEST_CASE(external_constructor) function test() external {} } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Constructor must be public or internal."); } BOOST_AUTO_TEST_CASE(invalid_array_as_statement) @@ -4856,7 +4856,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_as_statement) function test(uint k) { S[k]; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Integer constant expected."); } BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) @@ -4875,7 +4875,7 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"b\" not found or not visible after argument-dependent lookup in bytes memory"); } BOOST_AUTO_TEST_CASE(function_type) @@ -4923,7 +4923,7 @@ BOOST_AUTO_TEST_CASE(private_function_type) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\"."); } BOOST_AUTO_TEST_CASE(public_function_type) @@ -4935,7 +4935,7 @@ BOOST_AUTO_TEST_CASE(public_function_type) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\"."); } BOOST_AUTO_TEST_CASE(payable_internal_function_type) @@ -4945,7 +4945,7 @@ BOOST_AUTO_TEST_CASE(payable_internal_function_type) function (uint) internal payable returns (uint) x; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Only external function types can be payable."); } BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type) @@ -4958,7 +4958,7 @@ BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the \"payable\" modifier?"); } BOOST_AUTO_TEST_CASE(external_function_type_returning_internal) @@ -4968,7 +4968,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_returning_internal) function() external returns (function () internal) x; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type."); } BOOST_AUTO_TEST_CASE(external_function_type_taking_internal) @@ -4978,7 +4978,7 @@ BOOST_AUTO_TEST_CASE(external_function_type_taking_internal) function(function () internal) external x; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type."); } BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type) @@ -5004,7 +5004,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) @@ -5016,7 +5016,7 @@ BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal) @@ -5038,7 +5038,7 @@ BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Internal type is not allowed for public or external functions."); } BOOST_AUTO_TEST_CASE(function_type_arrays) @@ -5089,7 +5089,7 @@ BOOST_AUTO_TEST_CASE(delete_function_type_invalid) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid) @@ -5101,7 +5101,7 @@ BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); } BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter) @@ -5184,7 +5184,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue) uint public a = 0x42 << -8; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const -8"); } BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue) @@ -5194,7 +5194,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue) uint public a = 0x42 >> -8; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const -8"); } BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue) @@ -5204,7 +5204,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue) uint public a = 0x42 << 0x100000000; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const 4294967296"); } BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) @@ -5214,7 +5214,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) uint public a = 0x42 >> 0x100000000; } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const 4294967296"); } BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack) @@ -5409,7 +5409,7 @@ BOOST_AUTO_TEST_CASE(invalid_mobile_type) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Invalid mobile type."); } BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) From a8c047f48d61a3f27ea37f3b677cc4b1f3cbff3c Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Tue, 8 Aug 2017 18:58:06 -0300 Subject: [PATCH 048/163] Enforce commas in tuple syntax --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 5 +++-- test/libsolidity/SolidityParser.cpp | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3f3d2e9872c8..4609da9212e9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Metadata: Store experimental flag in metadata CBOR. Bugfixes: + * Parser: Enforce commas between array and tuple elements. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index a6d6e025ae07..066e3a29da9c 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1300,10 +1300,11 @@ ASTPointer Parser::parsePrimaryExpression() parserError("Expected expression (inline array elements cannot be omitted)."); else components.push_back(ASTPointer()); + if (m_scanner->currentToken() == oppositeToken) break; - else if (m_scanner->currentToken() == Token::Comma) - m_scanner->next(); + + expectToken(Token::Comma); } nodeFactory.markEndPosition(); expectToken(oppositeToken); diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 2be824d6baeb..3890ca21d459 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1187,6 +1187,18 @@ BOOST_AUTO_TEST_CASE(tuples) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(tuples_without_commas) +{ + char const* text = R"( + contract C { + function f() { + var a = (2 2); + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected token Comma"); +} + BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) { char const* text = R"( From da3ac8640328c15872630d5d86976f17480f9492 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 15:44:35 +0200 Subject: [PATCH 049/163] Warn about large storage structures. --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 42 +++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 3 ++ .../SolidityNameAndTypeResolution.cpp | 51 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4609da9212e9..cb6720777640 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Parser: Display previous visibility specifier in error if multiple are found. * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. + * Static Analyzer: Warn about large storage structures. * Metadata: Store experimental flag in metadata CBOR. Bugfixes: diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 46477e1e076b..ab1cbb5268c8 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -92,6 +92,17 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) // This is not a no-op, the entry might pre-exist. m_localVarUseCount[&_variable] += 0; } + else if (_variable.isStateVariable()) + { + set structsSeen; + if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64) + m_errorReporter.warning( + _variable.location(), + "Variable covers a large part of storage and thus makes collisions likely. " + "Either use mappings or dynamic arrays and allow their size to be increased only " + "in small quantities per transaction." + ); + } return true; } @@ -160,3 +171,34 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } + +bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set& _structsSeen) +{ + switch (_type.category()) + { + case Type::Category::Array: + { + auto const& t = dynamic_cast(_type); + return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length()); + } + case Type::Category::Struct: + { + auto const& t = dynamic_cast(_type); + bigint size = 1; + if (!_structsSeen.count(&t.structDefinition())) + { + _structsSeen.insert(&t.structDefinition()); + for (auto const& m: t.members(nullptr)) + size += structureSizeEstimate(*m.type, _structsSeen); + } + return size; + } + case Type::Category::Mapping: + { + return structureSizeEstimate(*dynamic_cast(_type).valueType(), _structsSeen); + } + default: + break; + } + return bigint(1); +} diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 21a487df484c..a3080b428a89 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -65,6 +65,9 @@ class StaticAnalyzer: private ASTConstVisitor virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; + /// @returns the size of this type in storage, including all sub-types. + static bigint structureSizeEstimate(Type const& _type, std::set& _structsSeen); + ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f7648bd7a954..5c01a245756f 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6536,6 +6536,57 @@ BOOST_AUTO_TEST_CASE(constructor_without_implementation) CHECK_ERROR(text, TypeError, "Constructor must be implemented if declared."); } +BOOST_AUTO_TEST_CASE(large_storage_array_fine) +{ + char const* text = R"( + contract C { + uint[2**64 - 1] x; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(large_storage_array_simple) +{ + char const* text = R"( + contract C { + uint[2**64] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_arrays_combined) +{ + char const* text = R"( + contract C { + uint[200][200][2**30][][2**30] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_arrays_struct) +{ + char const* text = R"( + contract C { + struct S { uint[2**30] x; uint[2**50] y; } + S[2**20] x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + +BOOST_AUTO_TEST_CASE(large_storage_array_mapping) +{ + char const* text = R"( + contract C { + mapping(uint => uint[2**100]) x; + } + )"; + CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); +} + BOOST_AUTO_TEST_CASE(library_function_without_implementation) { char const* text = R"( From a5ceaac8df80f89112bb4d7bbfd9da165d370aa3 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Mon, 17 Jul 2017 16:47:44 -0300 Subject: [PATCH 050/163] Improve override changes signature error message --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 32 +++++++++++++++++++++++++++- libsolidity/analysis/TypeChecker.h | 3 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 4609da9212e9..b4ca745fa53c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Parser: Display previous visibility specifier in error if multiple are found. * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. * Metadata: Store experimental flag in metadata CBOR. + * Type Checker: More detailed error message for invalid overrides. Bugfixes: * Parser: Enforce commas between array and tuple elements. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6852c13da7a0..a74332508da1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -290,7 +290,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr overriding->isPayable() != function->isPayable() || overridingType != functionType ) - m_errorReporter.typeError(overriding->location(), "Override changes extended function signature."); + overrideTypeError(*overriding, *function); } functions[name].push_back(function); } @@ -1950,3 +1950,33 @@ void TypeChecker::requireLValue(Expression const& _expression) m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue."); } + +void TypeChecker::overrideTypeError(FunctionDefinition const& function, FunctionDefinition const& super) +{ + string message; + + if (function.visibility() != super.visibility()) + message = "Overriding function visibility differs from extended function."; + else if (function.isDeclaredConst() && !super.isDeclaredConst()) + message = "Overriding function should not be declared constant."; + else if (!function.isDeclaredConst() && super.isDeclaredConst()) + message = "Overriding function should be declared constant."; + else if (function.isPayable() && !super.isPayable()) + message = "Overriding function should not be declared payable."; + else if (!function.isPayable() && super.isPayable()) + message = "Overriding function should be declared payable."; + + if (message.empty()) + { + FunctionType functionType(function); + FunctionType superType(super); + + if (functionType != superType) + message = "Overriding function return types differ from extended function."; + } + + if (message.empty()) + message = "Override changes extended function signature."; + + m_errorReporter.typeError(function.location(), message); +} diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index ee43d13aa41e..17dde81a2c9b 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -120,6 +120,9 @@ class TypeChecker: private ASTConstVisitor /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression); + /// Reports a type error with an appropiate message when overriden function signature differs. + void overrideTypeError(FunctionDefinition const& function, FunctionDefinition const& super); + ContractDefinition const* m_scope = nullptr; ErrorReporter& m_errorReporter; From ff5bb54e3c19bedfadd06dd60f032fd7cdc55389 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Mon, 17 Jul 2017 19:12:34 -0300 Subject: [PATCH 051/163] Use fully qualified name of super in message --- libsolidity/analysis/TypeChecker.cpp | 7 +++---- libsolidity/ast/AST.cpp | 9 +++++++++ libsolidity/ast/AST.h | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a74332508da1..9bccac122427 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1950,13 +1950,12 @@ void TypeChecker::requireLValue(Expression const& _expression) m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue."); } - void TypeChecker::overrideTypeError(FunctionDefinition const& function, FunctionDefinition const& super) { string message; if (function.visibility() != super.visibility()) - message = "Overriding function visibility differs from extended function."; + message = "Overriding function visibility differs from " + super.fullyQualifiedName() + "."; else if (function.isDeclaredConst() && !super.isDeclaredConst()) message = "Overriding function should not be declared constant."; else if (!function.isDeclaredConst() && super.isDeclaredConst()) @@ -1972,11 +1971,11 @@ void TypeChecker::overrideTypeError(FunctionDefinition const& function, Function FunctionType superType(super); if (functionType != superType) - message = "Overriding function return types differ from extended function."; + message = "Overriding function return types differ from " + super.fullyQualifiedName() + "."; } if (message.empty()) - message = "Override changes extended function signature."; + message = "Overriding function signature differs from " + super.fullyQualifiedName() + "."; m_errorReporter.typeError(function.location(), message); } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 1d68231ea8f5..e173237e061b 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -371,6 +371,15 @@ string FunctionDefinition::externalSignature() const return FunctionType(*this).externalSignature(); } +string FunctionDefinition::fullyQualifiedName() const +{ + auto const* contract = dynamic_cast(scope()); + solAssert(contract, "Enclosing scope of function definition was not set."); + + auto fname = name().empty() ? "" : name(); + return sourceUnitName() + ":" + contract->name() + "." + fname; +} + FunctionDefinitionAnnotation& FunctionDefinition::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index bdf20e819da5..d32cf57390a2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -613,6 +613,7 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public std::vector> const& modifiers() const { return m_functionModifiers; } std::vector> const& returnParameters() const { return m_returnParameters->parameters(); } Block const& body() const { solAssert(m_body, ""); return *m_body; } + std::string fullyQualifiedName() const; virtual bool isVisibleInContract() const override { return Declaration::isVisibleInContract() && !isConstructor() && !isFallback(); From f0dc5720551a12febbe1a61461218de24e18e07a Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Tue, 18 Jul 2017 01:10:57 -0300 Subject: [PATCH 052/163] Improve and add missing tests --- .../SolidityNameAndTypeResolution.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f7648bd7a954..49a23e138aa9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -924,16 +924,25 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility) contract B { function f() internal {} } contract C is B { function f() public {} } )"; - CHECK_ERROR(text, TypeError, "Override changes extended function signature."); + CHECK_ERROR(text, TypeError, "Overriding function visibility differs"); } -BOOST_AUTO_TEST_CASE(illegal_override_constness) +BOOST_AUTO_TEST_CASE(illegal_override_remove_constness) { char const* text = R"( contract B { function f() constant {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, "Override changes extended function signature."); + CHECK_ERROR(text, TypeError, "Overriding function should be declared constant."); +} + +BOOST_AUTO_TEST_CASE(illegal_override_add_constness) +{ + char const* text = R"( + contract B { function f() {} } + contract C is B { function f() constant {} } + )"; + CHECK_ERROR(text, TypeError, "Overriding function should not be declared constant."); } BOOST_AUTO_TEST_CASE(complex_inheritance) @@ -2491,7 +2500,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types) function f(uint a) returns (uint8) { } } )"; - CHECK_ERROR(sourceCode, TypeError, "Override changes extended function signature."); + CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ"); } BOOST_AUTO_TEST_CASE(multiple_constructors) @@ -4732,7 +4741,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable) contract B { function f() payable {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, "Override changes extended function signature."); + CHECK_ERROR(text, TypeError, "Overriding function should be declared payable."); } BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) @@ -4741,7 +4750,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) contract B { function f() {} } contract C is B { function f() payable {} } )"; - CHECK_ERROR(text, TypeError, "Override changes extended function signature."); + CHECK_ERROR(text, TypeError, "Overriding function should not be declared payable."); } BOOST_AUTO_TEST_CASE(function_variable_mixin) From a6949851f332df8d05bef3cb2cd45105a02100a5 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Wed, 19 Jul 2017 14:32:26 -0300 Subject: [PATCH 053/163] Refactor function override check to remove duplicate logic --- libsolidity/analysis/TypeChecker.cpp | 92 +++++++++++++++------------- libsolidity/analysis/TypeChecker.h | 5 +- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 9bccac122427..df791be9fab0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -277,21 +277,10 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr string const& name = function->name(); if (modifiers.count(name)) m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); - FunctionType functionType(*function); - // function should not change the return type + for (FunctionDefinition const* overriding: functions[name]) - { - FunctionType overridingType(*overriding); - if (!overridingType.hasEqualArgumentTypes(functionType)) - continue; - if ( - overriding->visibility() != function->visibility() || - overriding->isDeclaredConst() != function->isDeclaredConst() || - overriding->isPayable() != function->isPayable() || - overridingType != functionType - ) - overrideTypeError(*overriding, *function); - } + checkFunctionOverride(*overriding, *function); + functions[name].push_back(function); } for (ModifierDefinition const* modifier: contract->functionModifiers()) @@ -308,6 +297,51 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } +void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) +{ + FunctionType functionType(function); + FunctionType superType(super); + + if (!functionType.hasEqualArgumentTypes(superType)) + return; + + if (function.visibility() != super.visibility()) + m_errorReporter.typeError( + function.location(), + "Overriding function visibility differs from " + super.fullyQualifiedName() + "." + ); + + if (function.isDeclaredConst() && !super.isDeclaredConst()) + m_errorReporter.typeError( + function.location(), + "Overriding function should not be declared constant." + ); + + if (!function.isDeclaredConst() && super.isDeclaredConst()) + m_errorReporter.typeError( + function.location(), + "Overriding function should be declared constant." + ); + + if (function.isPayable() && !super.isPayable()) + m_errorReporter.typeError( + function.location(), + "Overriding function should not be declared payable." + ); + + if (!function.isPayable() && super.isPayable()) + m_errorReporter.typeError( + function.location(), + "Overriding function should be declared payable." + ); + + if (functionType != superType) + m_errorReporter.typeError( + function.location(), + "Overriding function return types differ from " + super.fullyQualifiedName() + "." + ); +} + void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract) { map>> externalDeclarations; @@ -1949,33 +1983,3 @@ void TypeChecker::requireLValue(Expression const& _expression) else if (!_expression.annotation().isLValue) m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue."); } - -void TypeChecker::overrideTypeError(FunctionDefinition const& function, FunctionDefinition const& super) -{ - string message; - - if (function.visibility() != super.visibility()) - message = "Overriding function visibility differs from " + super.fullyQualifiedName() + "."; - else if (function.isDeclaredConst() && !super.isDeclaredConst()) - message = "Overriding function should not be declared constant."; - else if (!function.isDeclaredConst() && super.isDeclaredConst()) - message = "Overriding function should be declared constant."; - else if (function.isPayable() && !super.isPayable()) - message = "Overriding function should not be declared payable."; - else if (!function.isPayable() && super.isPayable()) - message = "Overriding function should be declared payable."; - - if (message.empty()) - { - FunctionType functionType(function); - FunctionType superType(super); - - if (functionType != superType) - message = "Overriding function return types differ from " + super.fullyQualifiedName() + "."; - } - - if (message.empty()) - message = "Overriding function signature differs from " + super.fullyQualifiedName() + "."; - - m_errorReporter.typeError(function.location(), message); -} diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 17dde81a2c9b..f1004e922979 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -62,6 +62,8 @@ class TypeChecker: private ASTConstVisitor /// arguments and that there is at most one constructor. void checkContractDuplicateFunctions(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); + /// Reports a type error with an appropiate message if overriden function signature differs. + void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void checkContractAbstractFunctions(ContractDefinition const& _contract); void checkContractAbstractConstructors(ContractDefinition const& _contract); /// Checks that different functions with external visibility end up having different @@ -120,9 +122,6 @@ class TypeChecker: private ASTConstVisitor /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression); - /// Reports a type error with an appropiate message when overriden function signature differs. - void overrideTypeError(FunctionDefinition const& function, FunctionDefinition const& super); - ContractDefinition const* m_scope = nullptr; ErrorReporter& m_errorReporter; From d4997dd9a3ec10767d8ee289207fdf0ea6b45f0a Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Wed, 19 Jul 2017 19:22:36 -0300 Subject: [PATCH 054/163] Use a secondary location for function override errors --- libsolidity/analysis/TypeChecker.cpp | 39 +++++++++++---------------- libsolidity/analysis/TypeChecker.h | 1 + libsolidity/interface/ErrorReporter.h | 4 +-- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index df791be9fab0..2a9ca5a884f3 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -306,40 +306,31 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func return; if (function.visibility() != super.visibility()) - m_errorReporter.typeError( - function.location(), - "Overriding function visibility differs from " + super.fullyQualifiedName() + "." - ); + overrideError(function, super, "Overriding function visibility differs."); if (function.isDeclaredConst() && !super.isDeclaredConst()) - m_errorReporter.typeError( - function.location(), - "Overriding function should not be declared constant." - ); + overrideError(function, super, "Overriding function should not be declared constant."); if (!function.isDeclaredConst() && super.isDeclaredConst()) - m_errorReporter.typeError( - function.location(), - "Overriding function should be declared constant." - ); + overrideError(function, super, "Overriding function should be declared constant."); if (function.isPayable() && !super.isPayable()) - m_errorReporter.typeError( - function.location(), - "Overriding function should not be declared payable." - ); + overrideError(function, super, "Overriding function should not be declared payable."); if (!function.isPayable() && super.isPayable()) - m_errorReporter.typeError( - function.location(), - "Overriding function should be declared payable." - ); + overrideError(function, super, "Overriding function should be declared payable."); if (functionType != superType) - m_errorReporter.typeError( - function.location(), - "Overriding function return types differ from " + super.fullyQualifiedName() + "." - ); + overrideError(function, super, "Overriding function return types differ."); +} + +void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) +{ + m_errorReporter.typeError( + function.location(), + SecondarySourceLocation().append("Overriden function is here:", super.location()), + message + ); } void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index f1004e922979..f2e13765ddbb 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -64,6 +64,7 @@ class TypeChecker: private ASTConstVisitor void checkContractIllegalOverrides(ContractDefinition const& _contract); /// Reports a type error with an appropiate message if overriden function signature differs. void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); + void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); void checkContractAbstractConstructors(ContractDefinition const& _contract); /// Checks that different functions with external visibility end up having different diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 42b0c8b626f6..12f4e8dff328 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -75,8 +75,8 @@ class ErrorReporter void typeError( SourceLocation const& _location, - SecondarySourceLocation const& _secondaryLocation, - std::string const& _description + SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(), + std::string const& _description = std::string() ); void typeError(SourceLocation const& _location, std::string const& _description); From 3571db6e3f54476cdabaf4861100564ae4b00a6a Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Fri, 21 Jul 2017 01:11:16 -0300 Subject: [PATCH 055/163] Avoid duplicate errors due to function overrides --- libsolidity/analysis/TypeChecker.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2a9ca5a884f3..8dd45a900ae8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -308,19 +308,19 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (function.visibility() != super.visibility()) overrideError(function, super, "Overriding function visibility differs."); - if (function.isDeclaredConst() && !super.isDeclaredConst()) + else if (function.isDeclaredConst() && !super.isDeclaredConst()) overrideError(function, super, "Overriding function should not be declared constant."); - if (!function.isDeclaredConst() && super.isDeclaredConst()) + else if (!function.isDeclaredConst() && super.isDeclaredConst()) overrideError(function, super, "Overriding function should be declared constant."); - if (function.isPayable() && !super.isPayable()) + else if (function.isPayable() && !super.isPayable()) overrideError(function, super, "Overriding function should not be declared payable."); - if (!function.isPayable() && super.isPayable()) + else if (!function.isPayable() && super.isPayable()) overrideError(function, super, "Overriding function should be declared payable."); - if (functionType != superType) + else if (functionType != superType) overrideError(function, super, "Overriding function return types differ."); } From fdeb94a105f816325dd5901555e13f1e3bf381f6 Mon Sep 17 00:00:00 2001 From: Stu West Date: Sat, 12 Aug 2017 15:43:41 -0700 Subject: [PATCH 056/163] docs typo: remove duplicate period and unnecessary spaces in intro --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 1f1373cfb1ca..1a3cf638707d 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -35,7 +35,7 @@ Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including, version 0.5.0). This is to ensure that the contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general, pragmas are instructions for the compiler about how to treat the -source code (e.g. `pragma once `_). . +source code (e.g. `pragma once `_). A contract in the sense of Solidity is a collection of code (its *functions*) and data (its *state*) that resides at a specific address on the Ethereum From 77b26552b1ced81a1fda72148518e59292fdff52 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 12:41:45 +0200 Subject: [PATCH 057/163] Allow multi-dimensional arrays in interfaces. --- libsolidity/ast/Types.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a66ccda5daf7..8950bd75a59f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1529,8 +1529,6 @@ TypePointer ArrayType::interfaceType(bool _inLibrary) const TypePointer baseExt = m_baseType->interfaceType(_inLibrary); if (!baseExt) return TypePointer(); - if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized()) - return TypePointer(); if (isDynamicallySized()) return make_shared(DataLocation::Memory, baseExt); From bda410bb071e0d66f393aed3ee9f96d68a8fbb41 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 16:38:48 +0200 Subject: [PATCH 058/163] Helpers. --- libdevcore/CommonData.h | 7 +++++++ libdevcore/Whiskers.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index ab4bfe682b00..6f40d7be03ca 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -145,6 +145,13 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) return (prefix == HexPrefix::Add) ? "0x" + str : str; } +inline std::string toCompactHexWithPrefix(u256 val) +{ + std::ostringstream ret; + ret << std::hex << val; + return "0x" + ret.str(); +} + // Algorithms for string and string-like collections. /// Escapes a string into the C-string representation. diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp index 4bad8476c4a8..b0a4c755e2a9 100644 --- a/libdevcore/Whiskers.cpp +++ b/libdevcore/Whiskers.cpp @@ -90,7 +90,7 @@ string Whiskers::replace( string tagName(_match[1]); if (!tagName.empty()) { - assertThrow(_parameters.count(tagName), WhiskersError, "Tag " + tagName + " not found."); + assertThrow(_parameters.count(tagName), WhiskersError, "Value for tag " + tagName + " not provided."); return _parameters.at(tagName); } else From 42fe8a2cb1530a557365d47878144c05687b859b Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 16:43:26 +0200 Subject: [PATCH 059/163] New ABI encoder. --- libsolidity/codegen/ABIFunctions.cpp | 1074 ++++++++++++++++++++++++++ libsolidity/codegen/ABIFunctions.h | 172 +++++ 2 files changed, 1246 insertions(+) create mode 100644 libsolidity/codegen/ABIFunctions.cpp create mode 100644 libsolidity/codegen/ABIFunctions.h diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp new file mode 100644 index 000000000000..a2938ed796e9 --- /dev/null +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -0,0 +1,1074 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2017 + * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + */ + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +ABIFunctions::~ABIFunctions() +{ + // This throws an exception and thus might cause immediate termination, but hey, + // it's a failed assertion anyway :-) + solAssert(m_requestedFunctions.empty(), "Forgot to call ``requestedFunctions()``."); +} + +string ABIFunctions::tupleEncoder( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes +) +{ + // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> + + solAssert(!_givenTypes.empty(), ""); + size_t const headSize_ = headSize(_targetTypes); + + Whiskers encoder(R"( + { + let tail := add($headStart, ) + + := tail + } + )"); + encoder("headSize", to_string(headSize_)); + string encodeElements; + size_t headPos = 0; + size_t stackPos = 0; + for (size_t i = 0; i < _givenTypes.size(); ++i) + { + solAssert(_givenTypes[i], ""); + solAssert(_targetTypes[i], ""); + size_t sizeOnStack = _givenTypes[i]->sizeOnStack(); + string valueNames = ""; + for (size_t j = 0; j < sizeOnStack; j++) + valueNames += "$value" + to_string(stackPos++) + ", "; + bool dynamic = _targetTypes[i]->isDynamicallyEncoded(); + Whiskers elementTempl( + dynamic ? + string(R"( + mstore(add($headStart, ), sub(tail, $headStart)) + tail := ( tail) + )") : + string(R"( + ( add($headStart, )) + )") + ); + elementTempl("values", valueNames); + elementTempl("pos", to_string(headPos)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); + encodeElements += elementTempl.render(); + headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); + } + solAssert(headPos == headSize_, ""); + encoder("encodeElements", encodeElements); + encoder("deepestStackElement", stackPos > 0 ? "$value0" : "$headStart"); + + return encoder.render(); +} + +string ABIFunctions::requestedFunctions() +{ + string result; + for (auto const& f: m_requestedFunctions) + result += f.second; + m_requestedFunctions.clear(); + return result; +} + +string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) +{ + string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); + return createFunction(functionName, [&]() { + Whiskers templ(R"( + function (value) -> cleaned { + + } + )"); + templ("functionName", functionName); + switch (_type.category()) + { + case Type::Category::Integer: + { + IntegerType const& type = dynamic_cast(_type); + if (type.numBits() == 256) + templ("body", "cleaned := value"); + else if (type.isSigned()) + templ("body", "cleaned := signextend(" + to_string(type.numBits() / 8 - 1) + ", value)"); + else + templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")"); + break; + } + case Type::Category::RationalNumber: + templ("body", "cleaned := value"); + break; + case Type::Category::Bool: + templ("body", "cleaned := iszero(iszero(value))"); + break; + case Type::Category::FixedPoint: + solUnimplemented("Fixed point types not implemented."); + break; + case Type::Category::Array: + solAssert(false, "Array cleanup requested."); + break; + case Type::Category::Struct: + solAssert(false, "Struct cleanup requested."); + break; + case Type::Category::FixedBytes: + { + FixedBytesType const& type = dynamic_cast(_type); + if (type.numBytes() == 32) + templ("body", "cleaned := value"); + else if (type.numBytes() == 0) + templ("body", "cleaned := 0"); + else + { + size_t numBits = type.numBytes() * 8; + u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits); + templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")"); + } + break; + } + case Type::Category::Contract: + templ("body", "cleaned := " + cleanupFunction(IntegerType(0, IntegerType::Modifier::Address)) + "(value)"); + break; + case Type::Category::Enum: + { + size_t members = dynamic_cast(_type).numberOfMembers(); + solAssert(members > 0, "empty enum should have caused a parser error."); + Whiskers w("switch lt(value, ) case 0 { } cleaned := value"); + w("members", to_string(members)); + if (_revertOnFailure) + w("failure", "revert(0, 0)"); + else + w("failure", "invalid()"); + templ("body", w.render()); + break; + } + default: + solAssert(false, "Cleanup of type " + _type.identifier() + " requested."); + } + + return templ.render(); + }); +} + +string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) +{ + string functionName = + "convert_" + + _from.identifier() + + "_to_" + + _to.identifier(); + return createFunction(functionName, [&]() { + Whiskers templ(R"( + function (value) -> converted { + + } + )"); + templ("functionName", functionName); + string body; + auto toCategory = _to.category(); + auto fromCategory = _from.category(); + switch (fromCategory) + { + case Type::Category::Integer: + case Type::Category::RationalNumber: + case Type::Category::Contract: + { + if (RationalNumberType const* rational = dynamic_cast(&_from)) + solUnimplementedAssert(!rational->isFractional(), "Not yet implemented - FixedPointType."); + if (toCategory == Type::Category::FixedBytes) + { + solAssert( + fromCategory == Type::Category::Integer || fromCategory == Type::Category::RationalNumber, + "Invalid conversion to FixedBytesType requested." + ); + FixedBytesType const& toBytesType = dynamic_cast(_to); + body = + Whiskers("converted := ((value))") + ("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8)) + ("clean", cleanupFunction(_from)) + .render(); + } + else if (toCategory == Type::Category::Enum) + { + solAssert(_from.mobileType(), ""); + body = + Whiskers("converted := ((value))") + ("cleanEnum", cleanupFunction(_to, false)) + // "mobileType()" returns integer type for rational + ("cleanInt", cleanupFunction(*_from.mobileType())) + .render(); + } + else if (toCategory == Type::Category::FixedPoint) + { + solUnimplemented("Not yet implemented - FixedPointType."); + } + else + { + solAssert( + toCategory == Type::Category::Integer || + toCategory == Type::Category::Contract, + ""); + IntegerType const addressType(0, IntegerType::Modifier::Address); + IntegerType const& to = + toCategory == Type::Category::Integer ? + dynamic_cast(_to) : + addressType; + + // Clean according to the "to" type, except if this is + // a widening conversion. + IntegerType const* cleanupType = &to; + if (fromCategory != Type::Category::RationalNumber) + { + IntegerType const& from = + fromCategory == Type::Category::Integer ? + dynamic_cast(_from) : + addressType; + if (to.numBits() > from.numBits()) + cleanupType = &from; + } + body = + Whiskers("converted := (value)") + ("cleanInt", cleanupFunction(*cleanupType)) + .render(); + } + break; + } + case Type::Category::Bool: + { + solAssert(_from == _to, "Invalid conversion for bool."); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction(_from)) + .render(); + break; + } + case Type::Category::FixedPoint: + solUnimplemented("Fixed point types not implemented."); + break; + case Type::Category::Array: + solUnimplementedAssert(false, "Array conversion not implemented."); + break; + case Type::Category::Struct: + solUnimplementedAssert(false, "Struct conversion not implemented."); + break; + case Type::Category::FixedBytes: + { + FixedBytesType const& from = dynamic_cast(_from); + if (toCategory == Type::Category::Integer) + body = + Whiskers("converted := ((value))") + ("shift", shiftRightFunction(256 - from.numBytes() * 8, false)) + ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) + .render(); + else + { + // clear for conversion to longer bytes + solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction(from)) + .render(); + } + break; + } + case Type::Category::Function: + { + solAssert(false, "Conversion should not be called for function types."); + break; + } + case Type::Category::Enum: + { + solAssert(toCategory == Type::Category::Integer || _from == _to, ""); + EnumType const& enumType = dynamic_cast(_from); + body = + Whiskers("converted := (value)") + ("clean", cleanupFunction(enumType)) + .render(); + break; + } + case Type::Category::Tuple: + { + solUnimplementedAssert(false, "Tuple conversion not implemented."); + break; + } + default: + solAssert(false, ""); + } + + solAssert(!body.empty(), ""); + templ("body", body); + return templ.render(); + }); +} + +string ABIFunctions::cleanupCombinedExternalFunctionIdFunction() +{ + string functionName = "cleanup_combined_external_function_id"; + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (addr_and_selector) -> cleaned { + cleaned := (addr_and_selector) + } + )") + ("functionName", functionName) + ("clean", cleanupFunction(FixedBytesType(24))) + .render(); + }); +} + +string ABIFunctions::combineExternalFunctionIdFunction() +{ + string functionName = "combine_external_function_id"; + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (addr, selector) -> combined { + combined := (or((addr), and(selector, 0xffffffff))) + } + )") + ("functionName", functionName) + ("shl32", shiftLeftFunction(32)) + ("shl64", shiftLeftFunction(64)) + .render(); + }); +} + +string ABIFunctions::abiEncodingFunction( + Type const& _from, + Type const& _to, + bool _encodeAsLibraryTypes, + bool _compacted +) +{ + solUnimplementedAssert( + _to.mobileType() && + _to.mobileType()->interfaceType(_encodeAsLibraryTypes) && + _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), + "Encoding type \"" + _to.toString() + "\" not yet implemented." + ); + TypePointer toInterface = _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + Type const& to = *toInterface; + + if (_from.category() == Type::Category::StringLiteral) + return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes); + else if (auto toArray = dynamic_cast(&to)) + { + solAssert(_from.category() == Type::Category::Array, ""); + solAssert(to.dataStoredIn(DataLocation::Memory), ""); + ArrayType const& fromArray = dynamic_cast(_from); + if (fromArray.location() == DataLocation::CallData) + return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes); + else if (!fromArray.isByteArray() && ( + fromArray.location() == DataLocation::Memory || + fromArray.baseType()->storageBytes() > 16 + )) + return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes); + else if (fromArray.location() == DataLocation::Memory) + return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes); + else if (fromArray.location() == DataLocation::Storage) + return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes); + else + solAssert(false, ""); + } + else if (dynamic_cast(&to)) + { + solUnimplementedAssert(false, "Structs not yet implemented."); + } + else if (_from.category() == Type::Category::Function) + return abiEncodingFunctionFunctionType( + dynamic_cast(_from), + to, + _encodeAsLibraryTypes, + _compacted + ); + + solAssert(_from.sizeOnStack() == 1, ""); + solAssert(to.isValueType(), ""); + solAssert(to.calldataEncodedSize() == 32, ""); + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + return createFunction(functionName, [&]() { + solAssert(!to.isDynamicallyEncoded(), ""); + + Whiskers templ(R"( + function (value, pos) { + mstore(pos, ) + } + )"); + templ("functionName", functionName); + + if (_from.dataStoredIn(DataLocation::Storage) && to.isValueType()) + { + // special case: convert storage reference type to value type - this is only + // possible for library calls where we just forward the storage reference + solAssert(_encodeAsLibraryTypes, ""); + solAssert(to == IntegerType(256), ""); + templ("cleanupConvert", "value"); + } + else + { + if (_from == to) + templ("cleanupConvert", cleanupFunction(_from) + "(value)"); + else + templ("cleanupConvert", conversionFunction(_from, to) + "(value)"); + } + return templ.render(); + }); +} + +string ABIFunctions::abiEncodingFunctionCalldataArray( + Type const& _from, + Type const& _to, + bool _encodeAsLibraryTypes +) +{ + solAssert(_to.isDynamicallySized(), ""); + solAssert(_from.category() == Type::Category::Array, "Unknown dynamic type."); + solAssert(_to.category() == Type::Category::Array, "Unknown dynamic type."); + auto const& fromArrayType = dynamic_cast(_from); + auto const& toArrayType = dynamic_cast(_to); + + solAssert(fromArrayType.location() == DataLocation::CallData, ""); + + solAssert( + *fromArrayType.copyForLocation(DataLocation::Memory, true) == + *toArrayType.copyForLocation(DataLocation::Memory, true), + "" + ); + + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + return createFunction(functionName, [&]() { + solUnimplementedAssert(fromArrayType.isByteArray(), ""); + // TODO if this is not a byte array, we might just copy byte-by-byte anyway, + // because the encoding is position-independent, but we have to check that. + Whiskers templ(R"( + function (start, length, pos) -> end { + // might update pos + (start, pos, length) + end := add(pos, (length)) + } + )"); + templ("storeLength", _to.isDynamicallySized() ? "mstore(pos, length) pos := add(pos, 0x20)" : ""); + templ("functionName", functionName); + templ("copyFun", copyToMemoryFunction(true)); + templ("roundUpFun", roundUpFunction()); + return templ.render(); + }); +} + +string ABIFunctions::abiEncodingFunctionSimpleArray( + ArrayType const& _from, + ArrayType const& _to, + bool _encodeAsLibraryTypes +) +{ + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + + solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); + solAssert(_from.length() == _to.length(), ""); + solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.dataStoredIn(DataLocation::Storage), ""); + solAssert(!_from.isByteArray(), ""); + solAssert(_from.dataStoredIn(DataLocation::Memory) || _from.baseType()->storageBytes() > 16, ""); + + return createFunction(functionName, [&]() { + bool dynamic = _to.isDynamicallyEncoded(); + bool dynamicBase = _to.baseType()->isDynamicallyEncoded(); + bool inMemory = _from.dataStoredIn(DataLocation::Memory); + Whiskers templ( + dynamicBase ? + R"( + function (value, pos) { + let length := (value) + // might update pos + let headStart := pos + let tail := add(pos, mul(length, 0x20)) + let srcPtr := (value) + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + mstore(pos, sub(tail, headStart)) + tail := ((srcPtr), tail) + srcPtr := (srcPtr) + pos := add(pos, ) + } + pos := tail + + } + )" : + R"( + function (value, pos) { + let length := (value) + // might update pos + let srcPtr := (value) + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + ((srcPtr), pos) + srcPtr := (srcPtr) + pos := add(pos, ) + } + + } + )" + ); + templ("functionName", functionName); + templ("return", dynamic ? " -> end " : ""); + templ("assignEnd", dynamic ? "end := pos" : ""); + templ("lengthFun", arrayLengthFunction(_from)); + if (_to.isDynamicallySized()) + templ("storeLength", "mstore(pos, length) pos := add(pos, 0x20)"); + else + templ("storeLength", ""); + templ("dataAreaFun", arrayDataAreaFunction(_from)); + templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize())); + templ("encodeToMemoryFun", abiEncodingFunction( + *_from.baseType(), + *_to.baseType(), + _encodeAsLibraryTypes, + true + )); + templ("arrayElementAccess", inMemory ? "mload" : "sload"); + templ("nextArrayElement", nextArrayElementFunction(_from)); + return templ.render(); + }); +} + +string ABIFunctions::abiEncodingFunctionMemoryByteArray( + ArrayType const& _from, + ArrayType const& _to, + bool _encodeAsLibraryTypes +) +{ + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + + solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); + solAssert(_from.length() == _to.length(), ""); + solAssert(_from.dataStoredIn(DataLocation::Memory), ""); + solAssert(_from.isByteArray(), ""); + + return createFunction(functionName, [&]() { + solAssert(_to.isByteArray(), ""); + Whiskers templ(R"( + function (value, pos) -> end { + let length := (value) + mstore(pos, length) + (add(value, 0x20), add(pos, 0x20), length) + end := add(add(pos, 0x20), (length)) + } + )"); + templ("functionName", functionName); + templ("lengthFun", arrayLengthFunction(_from)); + templ("copyFun", copyToMemoryFunction(false)); + templ("roundUpFun", roundUpFunction()); + return templ.render(); + }); +} + +string ABIFunctions::abiEncodingFunctionCompactStorageArray( + ArrayType const& _from, + ArrayType const& _to, + bool _encodeAsLibraryTypes +) +{ + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + + solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); + solAssert(_from.length() == _to.length(), ""); + solAssert(_from.dataStoredIn(DataLocation::Storage), ""); + + return createFunction(functionName, [&]() { + if (_from.isByteArray()) + { + solAssert(_to.isByteArray(), ""); + Whiskers templ(R"( + function (value, pos) -> ret { + let slotValue := sload(value) + switch and(slotValue, 1) + case 0 { + // short byte array + let length := and(div(slotValue, 2), 0x7f) + mstore(pos, length) + mstore(add(pos, 0x20), and(slotValue, not(0xff))) + ret := add(pos, 0x40) + } + case 1 { + // long byte array + let length := div(slotValue, 2) + mstore(pos, length) + pos := add(pos, 0x20) + let dataPos := (value) + let i := 0 + for { } lt(i, length) { i := add(i, 0x20) } { + mstore(add(pos, i), sload(dataPos)) + dataPos := add(dataPos, 1) + } + ret := add(pos, i) + } + } + )"); + templ("functionName", functionName); + templ("arrayDataSlot", arrayDataAreaFunction(_from)); + return templ.render(); + } + else + { + // Multiple items per slot + solAssert(_from.baseType()->storageBytes() <= 16, ""); + solAssert(!_from.baseType()->isDynamicallyEncoded(), ""); + solAssert(_from.baseType()->isValueType(), ""); + bool dynamic = _to.isDynamicallyEncoded(); + size_t storageBytes = _from.baseType()->storageBytes(); + size_t itemsPerSlot = 32 / storageBytes; + // This always writes full slot contents to memory, which might be + // more than desired, i.e. it writes beyond the end of memory. + Whiskers templ( + R"( + function (value, pos) { + let length := (value) + // might update pos + let originalPos := pos + let srcPtr := (value) + for { let i := 0 } lt(i, length) { i := add(i, ) } + { + let data := sload(srcPtr) + <#items> + ((data), pos) + pos := add(pos, ) + + srcPtr := add(srcPtr, 1) + } + pos := add(originalPos, mul(length, )) + + } + )" + ); + templ("functionName", functionName); + templ("return", dynamic ? " -> end " : ""); + templ("assignEnd", dynamic ? "end := pos" : ""); + templ("lengthFun", arrayLengthFunction(_from)); + if (_to.isDynamicallySized()) + templ("storeLength", "mstore(pos, length) pos := add(pos, 0x20)"); + else + templ("storeLength", ""); + templ("dataArea", arrayDataAreaFunction(_from)); + templ("itemsPerSlot", to_string(itemsPerSlot)); + string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()); + templ("elementEncodedSize", elementEncodedSize); + string encodeToMemoryFun = abiEncodingFunction( + *_from.baseType(), + *_to.baseType(), + _encodeAsLibraryTypes, + true + ); + templ("encodeToMemoryFun", encodeToMemoryFun); + std::vector> items(itemsPerSlot); + for (size_t i = 0; i < itemsPerSlot; ++i) + items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8, false); + templ("items", items); + return templ.render(); + } + }); +} + +string ABIFunctions::abiEncodingFunctionStringLiteral( + Type const& _from, + Type const& _to, + bool _encodeAsLibraryTypes +) +{ + solAssert(_from.category() == Type::Category::StringLiteral, ""); + + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_encodeAsLibraryTypes ? "_library" : ""); + return createFunction(functionName, [&]() { + auto const& strType = dynamic_cast(_from); + string const& value = strType.value(); + solAssert(_from.sizeOnStack() == 0, ""); + + if (_to.isDynamicallySized()) + { + Whiskers templ(R"( + function (pos) -> end { + mstore(pos, ) + <#word> + mstore(add(pos, ), ) + + end := add(pos, ) + } + )"); + templ("functionName", functionName); + + // TODO this can make use of CODECOPY for large strings once we have that in JULIA + size_t words = (value.size() + 31) / 32; + templ("overallSize", to_string(32 + words * 32)); + templ("length", to_string(value.size())); + vector> wordParams(words); + for (size_t i = 0; i < words; ++i) + { + wordParams[i]["offset"] = to_string(32 + i * 32); + wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex(); + } + templ("word", wordParams); + return templ.render(); + } + else + { + solAssert(_to.category() == Type::Category::FixedBytes, ""); + solAssert(value.size() <= 32, ""); + Whiskers templ(R"( + function (pos) { + mstore(pos, ) + } + )"); + templ("functionName", functionName); + templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex()); + return templ.render(); + } + }); +} + +string ABIFunctions::abiEncodingFunctionFunctionType( + FunctionType const& _from, + Type const& _to, + bool _encodeAsLibraryTypes, + bool _compacted +) +{ + solAssert(_from.kind() == FunctionType::Kind::External, ""); + solAssert(_from == _to, ""); + + string functionName = + "abi_encode_" + + _from.identifier() + + "_to_" + + _to.identifier() + + (_compacted ? "_compacted" : "") + + (_encodeAsLibraryTypes ? "_library" : ""); + + if (_compacted) + { + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (addr_and_function_id, pos) { + mstore(pos, (addr_and_function_id)) + } + )") + ("functionName", functionName) + ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction()) + .render(); + }); + } + else + { + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (addr, function_id, pos) { + mstore(pos, (addr, function_id)) + } + )") + ("functionName", functionName) + ("combineExtFun", combineExternalFunctionIdFunction()) + .render(); + }); + } +} + +string ABIFunctions::copyToMemoryFunction(bool _fromCalldata) +{ + string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory"; + return createFunction(functionName, [&]() { + if (_fromCalldata) + { + return Whiskers(R"( + function (src, dst, length) { + calldatacopy(dst, src, length) + // clear end + mstore(add(dst, length), 0) + } + )") + ("functionName", functionName) + .render(); + } + else + { + return Whiskers(R"( + function (src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + switch eq(i, length) + case 0 { + // clear end + mstore(add(dst, length), 0) + } + } + )") + ("functionName", functionName) + .render(); + } + }); +} + +string ABIFunctions::shiftLeftFunction(size_t _numBits) +{ + string functionName = "shift_left_" + to_string(_numBits); + return createFunction(functionName, [&]() { + solAssert(_numBits < 256, ""); + return + Whiskers(R"(function (value) -> newValue { + newValue := mul(value, ) + })") + ("functionName", functionName) + ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) + .render(); + }); +} + +string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed) +{ + string functionName = "shift_right_" + to_string(_numBits) + (_signed ? "_signed" : "_unsigned"); + return createFunction(functionName, [&]() { + solAssert(_numBits < 256, ""); + return + Whiskers(R"(function (value) -> newValue { + newValue :=
(value, ) + })") + ("functionName", functionName) + ("div", _signed ? "sdiv" : "div") + ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits)) + .render(); + }); +} + +string ABIFunctions::roundUpFunction() +{ + string functionName = "round_up_to_mul_of_32"; + return createFunction(functionName, [&]() { + return + Whiskers(R"(function (value) -> result { + result := and(add(value, 31), not(31)) + })") + ("functionName", functionName) + .render(); + }); +} + +string ABIFunctions::arrayLengthFunction(ArrayType const& _type) +{ + string functionName = "array_length_" + _type.identifier(); + return createFunction(functionName, [&]() { + Whiskers w(R"( + function (value) -> length { + + } + )"); + w("functionName", functionName); + string body; + if (!_type.isDynamicallySized()) + body = "length := " + toCompactHexWithPrefix(_type.length()); + else + { + switch (_type.location()) + { + case DataLocation::CallData: + solAssert(false, "called regular array length function on calldata array"); + break; + case DataLocation::Memory: + body = "length := mload(value)"; + break; + case DataLocation::Storage: + if (_type.isByteArray()) + { + // Retrieve length both for in-place strings and off-place strings: + // Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2 + // i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it + // computes (x & (-1)) / 2, which is equivalent to just x / 2. + body = R"( + length := sload(value) + let mask := sub(mul(0x100, iszero(and(length, 1))), 1) + length := div(and(length, mask), 2) + )"; + } + else + body = "length := sload(value)"; + break; + } + } + solAssert(!body.empty(), ""); + w("body", body); + return w.render(); + }); +} + +string ABIFunctions::arrayDataAreaFunction(ArrayType const& _type) +{ + string functionName = "array_dataslot_" + _type.identifier(); + return createFunction(functionName, [&]() { + if (_type.dataStoredIn(DataLocation::Memory)) + { + if (_type.isDynamicallySized()) + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + else + return Whiskers(R"( + function (memPtr) -> dataPtr { + dataPtr := memPtr + } + )") + ("functionName", functionName) + .render(); + } + else if (_type.dataStoredIn(DataLocation::Storage)) + { + if (_type.isDynamicallySized()) + { + Whiskers w(R"( + function (slot) -> dataSlot { + mstore(0, slot) + dataSlot := keccak256(0, 0x20) + } + )"); + w("functionName", functionName); + return w.render(); + } + else + { + Whiskers w(R"( + function (slot) -> dataSlot { + dataSlot := slot + } + )"); + w("functionName", functionName); + return w.render(); + } + } + else + { + // Not used for calldata + solAssert(false, ""); + } + }); +} + +string ABIFunctions::nextArrayElementFunction(ArrayType const& _type) +{ + solAssert(!_type.isByteArray(), ""); + solAssert( + _type.location() == DataLocation::Memory || + _type.location() == DataLocation::Storage, + "" + ); + solAssert( + _type.location() == DataLocation::Memory || + _type.baseType()->storageBytes() > 16, + "" + ); + string functionName = "array_nextElement_" + _type.identifier(); + return createFunction(functionName, [&]() { + if (_type.location() == DataLocation::Memory) + return Whiskers(R"( + function (memPtr) -> nextPtr { + nextPtr := add(memPtr, 0x20) + } + )") + ("functionName", functionName) + .render(); + else if (_type.location() == DataLocation::Storage) + return Whiskers(R"( + function (slot) -> nextSlot { + nextSlot := add(slot, 1) + } + )") + ("functionName", functionName) + .render(); + else + solAssert(false, ""); + }); +} + +string ABIFunctions::createFunction(string const& _name, function const& _creator) +{ + if (!m_requestedFunctions.count(_name)) + { + auto fun = _creator(); + solAssert(!fun.empty(), ""); + m_requestedFunctions[_name] = fun; + } + return _name; +} + +size_t ABIFunctions::headSize(TypePointers const& _targetTypes) +{ + size_t headSize = 0; + for (auto const& t: _targetTypes) + { + if (t->isDynamicallyEncoded()) + headSize += 0x20; + else + { + solAssert(t->calldataEncodedSize() > 0, ""); + headSize += t->calldataEncodedSize(); + } + } + + return headSize; +} diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h new file mode 100644 index 000000000000..76f4b46774c1 --- /dev/null +++ b/libsolidity/codegen/ABIFunctions.h @@ -0,0 +1,172 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2017 + * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace dev { +namespace solidity { + +class Type; +class ArrayType; +class StructType; +class FunctionType; +using TypePointer = std::shared_ptr; +using TypePointers = std::vector; + +/// +/// Class to generate encoding and decoding functions. Also maintains a collection +/// of "functions to be generated" in order to avoid generating the same function +/// multiple times. +/// +/// Make sure to include the result of ``requestedFunctions()`` to a block that +/// is visible from the code that was generated here. +class ABIFunctions +{ +public: + ~ABIFunctions(); + + /// @returns assembly code block to ABI-encode values of @a _givenTypes residing on the stack + /// into memory, converting the types to @a _targetTypes on the fly. + /// Assumed variables to be present: <$value0> <$value1> ... <$value(n-1)> <$headStart> + /// Does not allocate memory (does not change the memory head pointer), but writes + /// to memory starting at $headStart and an unrestricted amount after that. + /// Assigns the end of encoded memory either to $value0 or (if that is not present) + /// to $headStart. + std::string tupleEncoder( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes = false + ); + + /// @returns auxiliary functions referenced from the block generated in @a tupleEncoder + std::string requestedFunctions(); + +private: + /// @returns the name of the cleanup function for the given type and + /// adds its implementation to the requested functions. + /// @param _revertOnFailure if true, causes revert on invalid data, + /// otherwise an assertion failure. + std::string cleanupFunction(Type const& _type, bool _revertOnFailure = false); + + /// @returns the name of the function that converts a value of type @a _from + /// to a value of type @a _to. The resulting vale is guaranteed to be in range + /// (i.e. "clean"). Asserts on failure. + std::string conversionFunction(Type const& _from, Type const& _to); + + std::string cleanupCombinedExternalFunctionIdFunction(); + + /// @returns a function that combines the address and selector to a single value + /// for use in the ABI. + std::string combineExternalFunctionIdFunction(); + + /// @returns the name of the ABI encoding function with the given type + /// and queues the generation of the function to the requested functions. + /// @param _compacted if true, the input value was just loaded from storage + /// or memory and thus might be compacted into a single slot (depending on the type). + std::string abiEncodingFunction( + Type const& _givenType, + Type const& _targetType, + bool _encodeAsLibraryTypes, + bool _compacted + ); + /// Part of @a abiEncodingFunction for array target type and given calldata array. + std::string abiEncodingFunctionCalldataArray( + Type const& _givenType, + Type const& _targetType, + bool _encodeAsLibraryTypes + ); + /// Part of @a abiEncodingFunction for array target type and given memory array or + /// a given storage array with one item per slot. + std::string abiEncodingFunctionSimpleArray( + ArrayType const& _givenType, + ArrayType const& _targetType, + bool _encodeAsLibraryTypes + ); + std::string abiEncodingFunctionMemoryByteArray( + ArrayType const& _givenType, + ArrayType const& _targetType, + bool _encodeAsLibraryTypes + ); + /// Part of @a abiEncodingFunction for array target type and given storage array + /// where multiple items are packed into the same storage slot. + std::string abiEncodingFunctionCompactStorageArray( + ArrayType const& _givenType, + ArrayType const& _targetType, + bool _encodeAsLibraryTypes + ); + + // @returns the name of the ABI encoding function with the given type + // and queues the generation of the function to the requested functions. + // Case for _givenType being a string literal + std::string abiEncodingFunctionStringLiteral( + Type const& _givenType, + Type const& _targetType, + bool _encodeAsLibraryTypes + ); + + std::string abiEncodingFunctionFunctionType( + FunctionType const& _from, + Type const& _to, + bool _encodeAsLibraryTypes, + bool _compacted + ); + + /// @returns a function that copies raw bytes of dynamic length from calldata + /// or memory to memory. + /// Pads with zeros and might write more than exactly length. + std::string copyToMemoryFunction(bool _fromCalldata); + + std::string shiftLeftFunction(size_t _numBits); + std::string shiftRightFunction(size_t _numBits, bool _signed); + /// @returns the name of a function that rounds its input to the next multiple + /// of 32 or the input if it is a multiple of 32. + std::string roundUpFunction(); + + std::string arrayLengthFunction(ArrayType const& _type); + /// @returns the name of a function that converts a storage slot number + /// or a memory pointer to the slot number / memory pointer for the data position of an array + /// which is stored in that slot / memory area. + std::string arrayDataAreaFunction(ArrayType const& _type); + /// @returns the name of a function that advances an array data pointer to the next element. + /// Only works for memory arrays and storage arrays that store one item per slot. + std::string nextArrayElementFunction(ArrayType const& _type); + + /// Helper function that uses @a _creator to create a function and add it to + /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both + /// cases. + std::string createFunction(std::string const& _name, std::function const& _creator); + + /// @returns the size of the static part of the encoding of the given types. + size_t headSize(TypePointers const& _targetTypes); + + /// Map from function name to code for a multi-use function. + std::map m_requestedFunctions; +}; + +} +} From 4630b3315aa249508036998e4ed122b5ba260ba1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Aug 2017 16:46:09 +0200 Subject: [PATCH 060/163] Interface for new ABI encoder. --- libsolidity/codegen/CompilerUtils.cpp | 32 +++++++++++++++++++++++++++ libsolidity/codegen/CompilerUtils.h | 8 +++++++ 2 files changed, 40 insertions(+) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 782aad9d225e..d2dab7d7690f 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace std; @@ -180,8 +181,17 @@ void CompilerUtils::encodeToMemory( t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); } + bool activateNewEncoder = false; if (_givenTypes.empty()) return; + else if (activateNewEncoder && _padToWordBoundaries && !_copyDynamicDataInPlace) + { + // Use the new JULIA-based encoding function + auto stackHeightBefore = m_context.stackHeight(); + abiEncode(_givenTypes, targetTypes, _encodeAsLibraryTypes); + solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); + return; + } // Stack during operation: // ... ... @@ -289,6 +299,28 @@ void CompilerUtils::encodeToMemory( popStackSlots(argSize + dynPointers + 1); } +void CompilerUtils::abiEncode( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes +) +{ + // stack: <$value0> <$value1> ... <$value(n-1)> <$headStart> + + vector variables; + size_t numValues = sizeOnStack(_givenTypes); + for (size_t i = 0; i < numValues; ++i) + variables.push_back("$value" + to_string(i)); + variables.push_back("$headStart"); + + ABIFunctions funs; + string routine = funs.tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes); + routine += funs.requestedFunctions(); + m_context.appendInlineAssembly("{" + routine + "}", variables); + // Remove everyhing except for "value0" / the final memory pointer. + popStackSlots(numValues); +} + void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) { auto repeat = m_context.newTag(); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index fb169463a96b..0942778860f5 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -103,6 +103,14 @@ class CompilerUtils bool _encodeAsLibraryTypes = false ); + /// Special case of @a encodeToMemory which assumes that everything is padded to words + /// and dynamic data is not copied in place (i.e. a proper ABI encoding). + void abiEncode( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes = false + ); + /// Zero-initialises (the data part of) an already allocated memory array. /// Length has to be nonzero! /// Stack pre: From 38446a9669b3ddeadb3a088bad98ef948fa5adc6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 3 Aug 2017 15:06:59 +0200 Subject: [PATCH 061/163] ABI encoder tests. --- test/libsolidity/ABIEncoderTests.cpp | 86 +++++++++++++++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 21 +++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 44c673c5d112..242050690a10 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -126,6 +126,73 @@ BOOST_AUTO_TEST_CASE(conversion) )); } +BOOST_AUTO_TEST_CASE(memory_array_one_dim) +{ + char const* sourceCode = R"( + contract C { + event E(uint a, int16[] b, uint c); + function f() { + int16[] memory x = new int16[](3); + assembly { + for { let i := 0 } lt(i, 3) { i := add(i, 1) } { + mstore(add(x, mul(add(i, 1), 0x20)), add(0xfffffffe, i)) + } + } + E(10, x, 11); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256(-2), u256(-1), u256(0))); +} + +BOOST_AUTO_TEST_CASE(memory_array_two_dim) +{ + char const* sourceCode = R"( + contract C { + event E(uint a, int16[][2] b, uint c); + function f() { + int16[][2] memory x; + x[0] = new int16[](3); + x[1] = new int16[](2); + x[0][0] = 7; + x[0][1] = int16(0x010203040506); + x[0][2] = -1; + x[1][0] = 4; + x[1][1] = 5; + E(10, x, 11); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 0x40, 0xc0, 3, 7, 0x0506, u256(-1), 2, 4, 5)); +} + +BOOST_AUTO_TEST_CASE(memory_byte_array) +{ + char const* sourceCode = R"( + contract C { + event E(uint a, bytes[] b, uint c); + function f() { + bytes[] memory x = new bytes[](2); + x[0] = "abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"; + x[1] = "abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw"; + E(10, x, 11); + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 10, 0x60, 11, + 2, 0x40, 0xc0, + 66, string("abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"), + 63, string("abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw") + )); +} + BOOST_AUTO_TEST_CASE(storage_byte_array) { char const* sourceCode = R"( @@ -252,6 +319,25 @@ BOOST_AUTO_TEST_CASE(external_function_cleanup) REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1)))); } +BOOST_AUTO_TEST_CASE(calldata) +{ + char const* sourceCode = R"( + contract C { + event E(bytes); + function f(bytes a) external { + E(a); + } + } + )"; + compileAndRun(sourceCode); + string s("abcdef"); + string t("abcdefgggggggggggggggggggggggggggggggggggggggghhheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeggg"); + callContractFunction("f(bytes)", 0x20, s.size(), s); + REQUIRE_LOG_DATA(encodeArgs(0x20, s.size(), s)); + callContractFunction("f(bytes)", 0x20, t.size(), t); + REQUIRE_LOG_DATA(encodeArgs(0x20, t.size(), t)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 166c46601893..73dd7d22ca83 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3128,7 +3128,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes()); + BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes())); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } @@ -3152,7 +3152,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC"))); + BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 3, string("ABC")))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } @@ -4432,10 +4432,12 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) // NOTE: This does not really test copying from storage to ABI directly, // because it will always copy to memory first. char const* sourceCode = R"( + pragma experimental ABIEncoderV2; contract c { uint8[] x; uint16[] y; uint24[] z; + uint24[][] w; function test1() returns (uint8[]) { for (uint i = 0; i < 101; ++i) x.push(uint8(i)); @@ -4451,6 +4453,13 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) z.push(uint24(i)); return z; } + function test4() returns (uint24[][]) { + w.length = 5; + for (uint i = 0; i < 5; ++i) + for (uint j = 0; j < 101; ++j) + w[i].push(uint24(j)); + return w; + } } )"; compileAndRun(sourceCode); @@ -4460,6 +4469,14 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) BOOST_CHECK(callContractFunction("test1()") == encodeArgs(0x20, 101) + valueSequence); BOOST_CHECK(callContractFunction("test2()") == encodeArgs(0x20, 101) + valueSequence); BOOST_CHECK(callContractFunction("test3()") == encodeArgs(0x20, 101) + valueSequence); + BOOST_CHECK(callContractFunction("test4()") == + encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + ); } BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed) From ee6f56d6418d1d25690702adf55a00733517062c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Aug 2017 17:54:08 +0200 Subject: [PATCH 062/163] Remove unused magic global feature. --- libsolidity/codegen/CompilerContext.cpp | 5 ----- libsolidity/codegen/CompilerContext.h | 7 ++----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index bc4de3ee2806..ed780d0bd51c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -44,11 +44,6 @@ namespace dev namespace solidity { -void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration) -{ - m_magicGlobals.insert(&_declaration); -} - void CompilerContext::addStateVariable( VariableDeclaration const& _declaration, u256 const& _storageOffset, diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 13821f67d7d8..af5230bd4f53 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -48,7 +48,7 @@ namespace solidity { class CompilerContext { public: - CompilerContext(CompilerContext* _runtimeContext = nullptr): + explicit CompilerContext(CompilerContext* _runtimeContext = nullptr): m_asm(std::make_shared()), m_runtimeContext(_runtimeContext) { @@ -56,7 +56,7 @@ class CompilerContext m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } - void addMagicGlobal(MagicVariableDeclaration const& _declaration); + void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(VariableDeclaration const& _declaration); @@ -68,7 +68,6 @@ class CompilerContext void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); } unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); } - bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; } bool isLocalVariable(Declaration const* _declaration) const; bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; } @@ -265,8 +264,6 @@ class CompilerContext } m_functionCompilationQueue; eth::AssemblyPointer m_asm; - /// Magic global variables like msg, tx or this, distinguished by type. - std::set m_magicGlobals; /// Other already compiled contracts to be used in contract creation calls. std::map m_compiledContracts; /// Storage offsets of state variables From d1ad62fccc02dba20129d59a81f260b9ac6b41de Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Aug 2017 17:56:04 +0200 Subject: [PATCH 063/163] Experimental feature switch for ABI encoder. --- libsolidity/ast/ExperimentalFeatures.h | 8 ++++++-- libsolidity/codegen/CompilerContext.h | 4 ++++ libsolidity/codegen/CompilerUtils.cpp | 7 +++++-- libsolidity/codegen/ContractCompiler.cpp | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 04b2630023ec..0c03ea4ad421 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -29,15 +29,19 @@ namespace solidity enum class ExperimentalFeature { + ABIEncoderV2, // new ABI encoder that makes use of JULIA Test, TestOnlyAnalysis }; -static const std::map ExperimentalFeatureOnlyAnalysis = { +static const std::map ExperimentalFeatureOnlyAnalysis = +{ { ExperimentalFeature::TestOnlyAnalysis, true }, }; -static const std::map ExperimentalFeatureNames = { +static const std::map ExperimentalFeatureNames = +{ + { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, }; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index af5230bd4f53..583360eae422 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -56,6 +56,8 @@ class CompilerContext m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); } + void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } + bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); @@ -264,6 +266,8 @@ class CompilerContext } m_functionCompilationQueue; eth::AssemblyPointer m_asm; + /// Activated experimental features. + std::set m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. std::map m_compiledContracts; /// Storage offsets of state variables diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d2dab7d7690f..a0fc5d55e640 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -181,10 +181,13 @@ void CompilerUtils::encodeToMemory( t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); } - bool activateNewEncoder = false; if (_givenTypes.empty()) return; - else if (activateNewEncoder && _padToWordBoundaries && !_copyDynamicDataInPlace) + else if ( + _padToWordBoundaries && + !_copyDynamicDataInPlace && + m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) + ) { // Use the new JULIA-based encoding function auto stackHeightBefore = m_context.stackHeight(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index fd0998d49781..29a22faeb094 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -100,6 +100,7 @@ void ContractCompiler::initializeContext( map const& _compiledContracts ) { + m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); m_context.setCompiledContracts(_compiledContracts); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); CompilerUtils(m_context).initialiseFreeMemoryPointer(); From 63b556b2063acfe84f16973bd5b0429d70f7fb93 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Aug 2017 18:27:57 +0200 Subject: [PATCH 064/163] Test both encoders. --- test/libsolidity/ABIEncoderTests.cpp | 214 +++++++++++++++++---------- 1 file changed, 137 insertions(+), 77 deletions(-) diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 242050690a10..297c4ef08015 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -42,11 +42,36 @@ namespace test BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(DATA)); \ } while (false) +static string const NewEncoderPragma = "pragma experimental ABIEncoderV2;\n"; + +#define NEW_ENCODER(CODE) \ +{ \ + sourceCode = NewEncoderPragma + sourceCode; \ + { CODE } \ +} + +#define BOTH_ENCODERS(CODE) \ +{ \ + { CODE } \ + NEW_ENCODER(CODE) \ +} + BOOST_FIXTURE_TEST_SUITE(ABIEncoderTest, SolidityExecutionFramework) +BOOST_AUTO_TEST_CASE(both_encoders_macro) +{ + // This tests that the "both encoders macro" at least runs twice and + // modifies the source. + string sourceCode; + int runs = 0; + BOTH_ENCODERS(runs++;) + BOOST_CHECK(sourceCode == NewEncoderPragma); + BOOST_CHECK_EQUAL(runs, 2); +} + BOOST_AUTO_TEST_CASE(value_types) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(uint a, uint16 b, uint24 c, int24 d, bytes3 x, bool, C); function f() { @@ -59,16 +84,18 @@ BOOST_AUTO_TEST_CASE(value_types) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, u160(u256(-5)) - )); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, u160(u256(-5)) + )); + ) } BOOST_AUTO_TEST_CASE(string_literal) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(string, bytes20, string); function f() { @@ -76,19 +103,21 @@ BOOST_AUTO_TEST_CASE(string_literal) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - 0x60, string("abcde"), 0xa0, - 6, string("abcdef"), - 0x8b, string("abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl") - )); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x60, string("abcde"), 0xa0, + 6, string("abcdef"), + 0x8b, string("abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl") + )); + ) } BOOST_AUTO_TEST_CASE(enum_type_cleanup) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { enum E { A, B } function f(uint x) returns (E en) { @@ -96,15 +125,17 @@ BOOST_AUTO_TEST_CASE(enum_type_cleanup) } } )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("f(uint256)", 0) == encodeArgs(0)); - BOOST_CHECK(callContractFunction("f(uint256)", 1) == encodeArgs(1)); - BOOST_CHECK(callContractFunction("f(uint256)", 2) == encodeArgs()); + BOTH_ENCODERS( + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", 0) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("f(uint256)", 1) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(uint256)", 2) == encodeArgs()); + ) } BOOST_AUTO_TEST_CASE(conversion) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(bytes4, bytes4, uint16, uint8, int16, int8); function f() { @@ -118,17 +149,19 @@ BOOST_AUTO_TEST_CASE(conversion) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - string(3, 0) + string("\x0a"), string("\xf1\xf2"), - 0xff, 0xff, u256(-1), u256(1) - )); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + string(3, 0) + string("\x0a"), string("\xf1\xf2"), + 0xff, 0xff, u256(-1), u256(1) + )); + ) } BOOST_AUTO_TEST_CASE(memory_array_one_dim) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(uint a, int16[] b, uint c); function f() { @@ -142,14 +175,20 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) } } )"; + compileAndRun(sourceCode); callContractFunction("f()"); + // The old encoder does not clean array elements. + REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256("0xfffffffe"), u256("0xffffffff"), u256("0x100000000"))); + + compileAndRun(NewEncoderPragma + sourceCode); + callContractFunction("f()"); REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256(-2), u256(-1), u256(0))); } BOOST_AUTO_TEST_CASE(memory_array_two_dim) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(uint a, int16[][2] b, uint c); function f() { @@ -165,14 +204,16 @@ BOOST_AUTO_TEST_CASE(memory_array_two_dim) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 0x40, 0xc0, 3, 7, 0x0506, u256(-1), 2, 4, 5)); + NEW_ENCODER( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 0x40, 0xc0, 3, 7, 0x0506, u256(-1), 2, 4, 5)); + ) } BOOST_AUTO_TEST_CASE(memory_byte_array) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(uint a, bytes[] b, uint c); function f() { @@ -183,19 +224,21 @@ BOOST_AUTO_TEST_CASE(memory_byte_array) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - 10, 0x60, 11, - 2, 0x40, 0xc0, - 66, string("abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"), - 63, string("abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw") - )); + NEW_ENCODER( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 10, 0x60, 11, + 2, 0x40, 0xc0, + 66, string("abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"), + 63, string("abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw") + )); + ) } BOOST_AUTO_TEST_CASE(storage_byte_array) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { bytes short; bytes long; @@ -207,18 +250,20 @@ BOOST_AUTO_TEST_CASE(storage_byte_array) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - 0x40, 0x80, - 31, string("123456789012345678901234567890a"), - 75, string("ffff123456789012345678901234567890afffffffff123456789012345678901234567890a") - )); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x40, 0x80, + 31, string("123456789012345678901234567890a"), + 75, string("ffff123456789012345678901234567890afffffffff123456789012345678901234567890a") + )); + ) } BOOST_AUTO_TEST_CASE(storage_array) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { address[3] addr; event E(address[3] a); @@ -232,14 +277,16 @@ BOOST_AUTO_TEST_CASE(storage_array) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3))); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3))); + ) } BOOST_AUTO_TEST_CASE(storage_array_dyn) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { address[] addr; event E(address[] a); @@ -251,14 +298,16 @@ BOOST_AUTO_TEST_CASE(storage_array_dyn) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(0x20, 3, u160(1), u160(2), u160(3))); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(0x20, 3, u160(1), u160(2), u160(3))); + ) } BOOST_AUTO_TEST_CASE(storage_array_compact) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { int72[] x; event E(int72[]); @@ -275,16 +324,18 @@ BOOST_AUTO_TEST_CASE(storage_array_compact) } } )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs( - 0x20, 8, u256(-1), 2, u256(-3), 4, u256(-5), 6, u256(-7), 8 - )); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs( + 0x20, 8, u256(-1), 2, u256(-3), 4, u256(-5), 6, u256(-7), 8 + )); + ) } BOOST_AUTO_TEST_CASE(external_function) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(function(uint) external returns (uint), function(uint) external returns (uint)); function(uint) external returns (uint) g; @@ -294,15 +345,17 @@ BOOST_AUTO_TEST_CASE(external_function) } } )"; - compileAndRun(sourceCode); - callContractFunction("f(uint256)"); - string functionIdF = asString(m_contractAddress.ref()) + asString(FixedHash<4>(dev::keccak256("f(uint256)")).ref()); - REQUIRE_LOG_DATA(encodeArgs(functionIdF, functionIdF)); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f(uint256)"); + string functionIdF = asString(m_contractAddress.ref()) + asString(FixedHash<4>(dev::keccak256("f(uint256)")).ref()); + REQUIRE_LOG_DATA(encodeArgs(functionIdF, functionIdF)); + ) } BOOST_AUTO_TEST_CASE(external_function_cleanup) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(function(uint) external returns (uint), function(uint) external returns (uint)); // This test relies on the fact that g is stored in slot zero. @@ -314,14 +367,16 @@ BOOST_AUTO_TEST_CASE(external_function_cleanup) } } )"; - compileAndRun(sourceCode); - callContractFunction("f(uint256)"); - REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1)))); + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f(uint256)"); + REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1)))); + ) } BOOST_AUTO_TEST_CASE(calldata) { - char const* sourceCode = R"( + string sourceCode = R"( contract C { event E(bytes); function f(bytes a) external { @@ -329,13 +384,18 @@ BOOST_AUTO_TEST_CASE(calldata) } } )"; - compileAndRun(sourceCode); string s("abcdef"); string t("abcdefgggggggggggggggggggggggggggggggggggggggghhheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeggg"); - callContractFunction("f(bytes)", 0x20, s.size(), s); - REQUIRE_LOG_DATA(encodeArgs(0x20, s.size(), s)); - callContractFunction("f(bytes)", 0x20, t.size(), t); - REQUIRE_LOG_DATA(encodeArgs(0x20, t.size(), t)); + bool newEncoder = false; + BOTH_ENCODERS( + compileAndRun(sourceCode); + callContractFunction("f(bytes)", 0x20, s.size(), s); + // The old encoder did not pad to multiples of 32 bytes + REQUIRE_LOG_DATA(encodeArgs(0x20, s.size()) + (newEncoder ? encodeArgs(s) : asBytes(s))); + callContractFunction("f(bytes)", 0x20, t.size(), t); + REQUIRE_LOG_DATA(encodeArgs(0x20, t.size()) + (newEncoder ? encodeArgs(t) : asBytes(t))); + newEncoder = true; + ) } BOOST_AUTO_TEST_SUITE_END() From 2a9ba937ba74ed8351b0590222ba2ac8a6e097c5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 1 Jul 2017 10:09:24 +0100 Subject: [PATCH 065/163] Replace constant/payable with StateMutability in AST --- libsolidity/ast/AST.h | 28 ++++++++---------- libsolidity/ast/ASTEnums.h | 52 ++++++++++++++++++++++++++++++++ libsolidity/ast/ASTForward.h | 1 - libsolidity/ast/Types.cpp | 38 ++++++++++++------------ libsolidity/ast/Types.h | 18 ++++++++---- libsolidity/parsing/Parser.cpp | 54 +++++++++++++++++++--------------- libsolidity/parsing/Parser.h | 5 ++-- libsolidity/parsing/Token.h | 1 + 8 files changed, 129 insertions(+), 68 deletions(-) create mode 100644 libsolidity/ast/ASTEnums.h diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d32cf57390a2..8a577c0caec4 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -584,21 +585,19 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public SourceLocation const& _location, ASTPointer const& _name, Declaration::Visibility _visibility, + StateMutability _stateMutability, bool _isConstructor, ASTPointer const& _documentation, ASTPointer const& _parameters, - bool _isDeclaredConst, std::vector> const& _modifiers, ASTPointer const& _returnParameters, - bool _isPayable, ASTPointer const& _body ): CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), Documented(_documentation), ImplementationOptional(_body != nullptr), + m_stateMutability(_stateMutability), m_isConstructor(_isConstructor), - m_isDeclaredConst(_isDeclaredConst), - m_isPayable(_isPayable), m_functionModifiers(_modifiers), m_body(_body) {} @@ -606,10 +605,11 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; + StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } bool isFallback() const { return name().empty(); } - bool isDeclaredConst() const { return m_isDeclaredConst; } - bool isPayable() const { return m_isPayable; } + bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; } + 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(); } Block const& body() const { solAssert(m_body, ""); return *m_body; } @@ -634,9 +634,8 @@ class FunctionDefinition: public CallableDeclaration, public Documented, public virtual FunctionDefinitionAnnotation& annotation() const override; private: + StateMutability m_stateMutability; bool m_isConstructor; - bool m_isDeclaredConst; - bool m_isPayable; std::vector> m_functionModifiers; ASTPointer m_body; }; @@ -896,11 +895,10 @@ class FunctionTypeName: public TypeName ASTPointer const& _parameterTypes, ASTPointer const& _returnTypes, Declaration::Visibility _visibility, - bool _isDeclaredConst, - bool _isPayable + StateMutability _stateMutability ): TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes), - m_visibility(_visibility), m_isDeclaredConst(_isDeclaredConst), m_isPayable(_isPayable) + m_visibility(_visibility), m_stateMutability(_stateMutability) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -914,15 +912,15 @@ class FunctionTypeName: public TypeName { return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility; } - bool isDeclaredConst() const { return m_isDeclaredConst; } - bool isPayable() const { return m_isPayable; } + StateMutability stateMutability() const { return m_stateMutability; } + bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; } + bool isPayable() const { return m_stateMutability == StateMutability::Payable; } private: ASTPointer m_parameterTypes; ASTPointer m_returnTypes; Declaration::Visibility m_visibility; - bool m_isDeclaredConst; - bool m_isPayable; + StateMutability m_stateMutability; }; /** diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h new file mode 100644 index 000000000000..f7c75878eaa9 --- /dev/null +++ b/libsolidity/ast/ASTEnums.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * @date 2017 + * Enums for AST classes. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +// How a function can mutate the EVM state. +enum class StateMutability { View, NonPayable, Payable }; + +inline std::string stateMutabilityToString(StateMutability const& _stateMutability) +{ + switch(_stateMutability) + { + case StateMutability::View: + return "view"; + case StateMutability::NonPayable: + return "nonpayable"; + case StateMutability::Payable: + return "payable"; + default: + solAssert(false, "Unknown state mutability."); + } +} + +} +} diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index cfeeaa58858c..157353681709 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -95,6 +95,5 @@ using ASTPointer = std::shared_ptr; using ASTString = std::string; - } } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a66ccda5daf7..35dddebb5387 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2000,8 +2000,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_kind(_isInternal ? Kind::Internal : Kind::External), - m_isConstant(_function.isDeclaredConst()), - m_isPayable(_isInternal ? false : _function.isPayable()), + m_stateMutability(_function.stateMutability()), m_declaration(&_function) { TypePointers params; @@ -2009,6 +2008,9 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal 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()) @@ -2030,7 +2032,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_kind(Kind::External), m_isConstant(true), m_declaration(&_varDecl) + m_kind(Kind::External), m_stateMutability(StateMutability::View), m_declaration(&_varDecl) { TypePointers paramTypes; vector paramNames; @@ -2090,7 +2092,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): } FunctionType::FunctionType(EventDefinition const& _event): - m_kind(Kind::Event), m_isConstant(true), m_declaration(&_event) + m_kind(Kind::Event), m_stateMutability(StateMutability::View), m_declaration(&_event) { TypePointers params; vector paramNames; @@ -2107,14 +2109,10 @@ FunctionType::FunctionType(EventDefinition const& _event): FunctionType::FunctionType(FunctionTypeName const& _typeName): m_kind(_typeName.visibility() == VariableDeclaration::Visibility::External ? Kind::External : Kind::Internal), - m_isConstant(_typeName.isDeclaredConst()), - m_isPayable(_typeName.isPayable()) + m_stateMutability(_typeName.stateMutability()) { if (_typeName.isPayable()) - { solAssert(m_kind == Kind::External, "Internal payable function type used."); - solAssert(!m_isConstant, "Payable constant function"); - } for (auto const& t: _typeName.parameterTypes()) { solAssert(t->annotation().type, "Type not set for parameter."); @@ -2241,8 +2239,8 @@ bool FunctionType::operator==(Type const& _other) const FunctionType const& other = dynamic_cast(_other); if ( m_kind != other.m_kind || - m_isConstant != other.isConstant() || - m_isPayable != other.isPayable() || + isConstant() != other.isConstant() || + isPayable() != other.isPayable() || m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size() ) @@ -2304,9 +2302,9 @@ string FunctionType::toString(bool _short) const for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); name += ")"; - if (m_isConstant) + if (isConstant()) name += " constant"; - if (m_isPayable) + if (isPayable()) name += " payable"; if (m_kind == Kind::External) name += " external"; @@ -2420,8 +2418,8 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const m_kind, m_arbitraryParameters, m_declaration, - m_isConstant, - m_isPayable + isConstant(), + isPayable() ); } @@ -2438,7 +2436,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con MemberList::MemberMap members; if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall) { - if (m_isPayable) + if (isPayable()) members.push_back(MemberList::Member( "value", make_shared( @@ -2604,8 +2602,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_kind, m_arbitraryParameters, m_declaration, - m_isConstant, - m_isPayable, + isConstant(), + isPayable(), m_gasSet || _setGas, m_valueSet || _setValue, m_bound @@ -2654,8 +2652,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) kind, m_arbitraryParameters, m_declaration, - m_isConstant, - m_isPayable, + isConstant(), + isPayable(), m_gasSet, m_valueSet, _bound diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 8c9232c6f935..64b07e4ef12c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -932,10 +933,15 @@ class FunctionType: public Type m_gasSet(_gasSet), m_valueSet(_valueSet), m_bound(_bound), - m_isConstant(_isConstant), - m_isPayable(_isPayable), m_declaration(_declaration) { + solAssert(!(_isConstant && _isPayable), ""); + if (_isPayable) + m_stateMutability = StateMutability::Payable; + else if (_isConstant) + m_stateMutability = StateMutability::View; + else + m_stateMutability = StateMutability::NonPayable; solAssert( !m_bound || !m_parameterTypes.empty(), "Attempted construction of bound function without self type" @@ -985,6 +991,7 @@ class FunctionType: public Type /// @returns true if the ABI is used for this call (only meaningful for external calls) bool isBareCall() const; Kind const& kind() const { return m_kind; } + StateMutability stateMutability() const { return m_stateMutability; } /// @returns the external signature of this function type given the function name std::string externalSignature() const; /// @returns the external identifier of this function (the hash of the signature). @@ -995,12 +1002,12 @@ class FunctionType: public Type return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - bool isConstant() const { return m_isConstant; } + bool isConstant() const { return m_stateMutability == StateMutability::View; } /// @returns true if the the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; - bool isPayable() const { return m_isPayable; } + bool isPayable() const { return m_stateMutability == StateMutability::Payable; } /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation ASTPointer documentation() const; @@ -1033,13 +1040,12 @@ class FunctionType: public Type std::vector m_parameterNames; std::vector m_returnParameterNames; Kind const m_kind; + StateMutability m_stateMutability = StateMutability::NonPayable; /// true if the function takes an arbitrary number of arguments of arbitrary types bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn) - bool m_isConstant = false; - bool m_isPayable = false; Declaration const* m_declaration = nullptr; }; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 066e3a29da9c..32bd096628ed 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -307,6 +307,19 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) return visibility; } +StateMutability Parser::parseStateMutability(Token::Value _token) +{ + StateMutability stateMutability(StateMutability::NonPayable); + if (_token == Token::Payable) + stateMutability = StateMutability::Payable; + else if (_token == Token::Constant) + stateMutability = StateMutability::View; + else + solAssert(false, "Invalid state mutability specifier."); + m_scanner->next(); + return stateMutability; +} + Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { FunctionHeaderParserResult result; @@ -321,23 +334,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN while (true) { Token::Value token = m_scanner->currentToken(); - if (token == Token::Constant) - { - if (result.isDeclaredConst) - parserError(string("Multiple \"constant\" specifiers.")); - - result.isDeclaredConst = true; - m_scanner->next(); - } - else if (m_scanner->currentToken() == Token::Payable) - { - if (result.isPayable) - parserError(string("Multiple \"payable\" specifiers.")); - - result.isPayable = true; - m_scanner->next(); - } - else if (_allowModifiers && token == Token::Identifier) + if (_allowModifiers && token == Token::Identifier) { // This can either be a modifier (function declaration) or the name of the // variable (function type name plus variable). @@ -364,6 +361,20 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN else result.visibility = parseVisibilitySpecifier(token); } + else if (Token::isStateMutabilitySpecifier(token)) + { + if (result.stateMutability != StateMutability::NonPayable) + { + parserError(string( + "State mutability already specified as \"" + + stateMutabilityToString(result.stateMutability) + + "\"." + )); + m_scanner->next(); + } + else + result.stateMutability = parseStateMutability(token); + } else break; } @@ -408,13 +419,12 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A return nodeFactory.createNode( header.name, header.visibility, + header.stateMutability, c_isConstructor, docstring, header.parameters, - header.isDeclaredConst, header.modifiers, header.returnParameters, - header.isPayable, block ); } @@ -425,8 +435,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A header.parameters, header.returnParameters, header.visibility, - header.isDeclaredConst, - header.isPayable + header.stateMutability ); type = parseTypeNameSuffix(type, nodeFactory); VarDeclParserOptions options; @@ -751,8 +760,7 @@ ASTPointer Parser::parseFunctionType() header.parameters, header.returnParameters, header.visibility, - header.isDeclaredConst, - header.isPayable + header.stateMutability ); } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 82ab91a64af4..97e60baa1715 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -60,8 +60,7 @@ class Parser: public ParserBase ASTPointer parameters; ASTPointer returnParameters; Declaration::Visibility visibility = Declaration::Visibility::Default; - bool isDeclaredConst = false; - bool isPayable = false; + StateMutability stateMutability = StateMutability::NonPayable; std::vector> modifiers; }; @@ -73,7 +72,7 @@ class Parser: public ParserBase ASTPointer parseContractDefinition(Token::Value _expectedKind); ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); - std::string visibilitySpecifierName(Declaration::Visibility _visibility); + StateMutability parseStateMutability(Token::Value _token); FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); ASTPointer parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer parseFunctionDefinition(ASTString const* _contractName); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 468cbcb71e1e..efbe5e9e1480 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -290,6 +290,7 @@ class Token static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } + static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } From 1c1388f24178767db8d519b490914b1b8c390bed Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 20:58:57 +0100 Subject: [PATCH 066/163] Update parser tests --- test/libsolidity/SolidityParser.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 3890ca21d459..75cad8d9c8fe 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -906,22 +906,23 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); } -BOOST_AUTO_TEST_CASE(multiple_payable_specifiers) +BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers) { char const* text = R"( contract c { function f() payable payable {} })"; - CHECK_PARSE_ERROR(text, "Multiple \"payable\" specifiers."); -} - -BOOST_AUTO_TEST_CASE(multiple_constant_specifiers) -{ - char const* text = R"( + CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\"."); + text = R"( contract c { function f() constant constant {} })"; - CHECK_PARSE_ERROR(text, "Multiple \"constant\" specifiers."); + CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\"."); + text = R"( + contract c { + function f() payable constant {} + })"; + CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\"."); } BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) From b225bf5d5364d72d75fab4a6843df665da28b842 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 21:00:39 +0100 Subject: [PATCH 067/163] Remove useless payable & constant typecheck --- libsolidity/analysis/TypeChecker.cpp | 2 -- test/libsolidity/SolidityNameAndTypeResolution.cpp | 9 --------- 2 files changed, 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8dd45a900ae8..dbc95c4f65b7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -500,8 +500,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.typeError(_function.location(), "Library functions cannot be payable."); if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); - if (_function.isDeclaredConst()) - m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time."); } for (ASTPointer const& var: _function.parameters() + _function.returnParameters()) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 13843afa0fca..51d60596441e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4771,15 +4771,6 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin) CHECK_ERROR(text, DeclarationError, "Identifier already declared."); } - -BOOST_AUTO_TEST_CASE(payable_constant_conflict) -{ - char const* text = R"( - contract C { function f() payable constant {} } - )"; - CHECK_ERROR(text, TypeError, "Functions cannot be constant and payable at the same time."); -} - BOOST_AUTO_TEST_CASE(calling_payable) { char const* text = R"( From a26a5f20ce46800f8deff5724b5c101bb9b5fc1d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 9 Aug 2017 21:52:25 +0100 Subject: [PATCH 068/163] Remove constant/payable in all function types --- libsolidity/ast/Types.cpp | 27 +++++++++------------- libsolidity/ast/Types.h | 17 ++++---------- libsolidity/codegen/ExpressionCompiler.cpp | 3 +-- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 35dddebb5387..6c2e55f6e384 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -477,8 +477,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons if (isAddress()) return { {"balance", make_shared(256)}, - {"call", make_shared(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, false, true)}, - {"callcode", make_shared(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)}, + {"call", make_shared(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)}, + {"callcode", make_shared(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)}, {"delegatecall", make_shared(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)}, {"send", make_shared(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, {"transfer", make_shared(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} @@ -2140,7 +2140,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c FunctionDefinition const* constructor = _contract.constructor(); TypePointers parameters; strings parameterNames; - bool payable = false; + StateMutability stateMutability = StateMutability::NonPayable; if (constructor) { @@ -2149,7 +2149,8 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c parameterNames.push_back(var->name()); parameters.push_back(var->annotation().type); } - payable = constructor->isPayable(); + if (constructor->isPayable()) + stateMutability = StateMutability::Payable; } return make_shared( parameters, @@ -2159,8 +2160,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c Kind::Creation, false, nullptr, - false, - payable + stateMutability ); } @@ -2418,8 +2418,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const m_kind, m_arbitraryParameters, m_declaration, - isConstant(), - isPayable() + m_stateMutability ); } @@ -2447,8 +2446,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con Kind::SetValue, false, nullptr, - false, - false, + StateMutability::NonPayable, m_gasSet, m_valueSet ) @@ -2465,8 +2463,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con Kind::SetGas, false, nullptr, - false, - false, + StateMutability::NonPayable, m_gasSet, m_valueSet ) @@ -2602,8 +2599,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_kind, m_arbitraryParameters, m_declaration, - isConstant(), - isPayable(), + m_stateMutability, m_gasSet || _setGas, m_valueSet || _setValue, m_bound @@ -2652,8 +2648,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) kind, m_arbitraryParameters, m_declaration, - isConstant(), - isPayable(), + m_stateMutability, m_gasSet, m_valueSet, _bound diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 64b07e4ef12c..56546a820ecd 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -890,8 +890,7 @@ class FunctionType: public Type strings const& _returnParameterTypes, Kind _kind = Kind::Internal, bool _arbitraryParameters = false, - bool _constant = false, - bool _payable = false + StateMutability _stateMutability = StateMutability::NonPayable ): FunctionType( parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), @@ -900,8 +899,7 @@ class FunctionType: public Type _kind, _arbitraryParameters, nullptr, - _constant, - _payable + _stateMutability ) { } @@ -918,8 +916,7 @@ class FunctionType: public Type Kind _kind = Kind::Internal, bool _arbitraryParameters = false, Declaration const* _declaration = nullptr, - bool _isConstant = false, - bool _isPayable = false, + StateMutability _stateMutability = StateMutability::NonPayable, bool _gasSet = false, bool _valueSet = false, bool _bound = false @@ -929,19 +926,13 @@ class FunctionType: public Type m_parameterNames(_parameterNames), m_returnParameterNames(_returnParameterNames), m_kind(_kind), + m_stateMutability(_stateMutability), m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet), m_bound(_bound), m_declaration(_declaration) { - solAssert(!(_isConstant && _isPayable), ""); - if (_isPayable) - m_stateMutability = StateMutability::Payable; - else if (_isConstant) - m_stateMutability = StateMutability::View; - else - m_stateMutability = StateMutability::NonPayable; solAssert( !m_bound || !m_parameterTypes.empty(), "Attempted construction of bound function without self type" diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 521d485f2143..55d35c44c728 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -645,8 +645,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) FunctionType::Kind::BareCall, false, nullptr, - false, - false, + StateMutability::NonPayable, true, true ), From bcce31b548870c323747085676f118b5b46ed234 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Aug 2017 18:58:56 +0200 Subject: [PATCH 069/163] Tests for recursion exploit in parser. --- test/libsolidity/SolidityParser.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 75cad8d9c8fe..23276fcde478 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1363,6 +1363,30 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(recursion_depth1) +{ + string text("contract C { bytes"); + for (size_t i = 0; i < 30000; i++) + text += "["; + CHECK_PARSE_ERROR(text.c_str(), "Maximum recursion depth reached during parsing"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth2) +{ + string text("contract C { function f() {"); + for (size_t i = 0; i < 30000; i++) + text += "{"; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth3) +{ + string text("contract C { function f() { uint x = f("); + for (size_t i = 0; i < 30000; i++) + text += "("; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) { char const* text = R"( From ed77f706a06c6b4ff1636d733e060ea592c2128a Mon Sep 17 00:00:00 2001 From: feliam Date: Mon, 14 Aug 2017 16:47:16 -0300 Subject: [PATCH 070/163] Update installing-solidity.rst --- docs/installing-solidity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 35eb2d93af45..ddc5c850ac60 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -135,7 +135,7 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer .. code:: bash - demerge ev-lang/solidity + emerge dev-lang/solidity .. _building-from-source: From 32e43477c336c9180eedd6ad968595e33ecde704 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Aug 2017 18:59:17 +0200 Subject: [PATCH 071/163] Prevent too deep recursion in parser. --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 75 +++++++++++++++++++++++++++++ libsolidity/parsing/Parser.h | 7 +++ test/libsolidity/SolidityParser.cpp | 12 +++++ 4 files changed, 95 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4f5615a94f37..256198f9036d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: Bugfixes: * Parser: Enforce commas between array and tuple elements. + * Parser: Limit maximum recursion depth. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 32bd096628ed..d3a6aa45dc24 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -64,10 +64,30 @@ class Parser::ASTNodeFactory SourceLocation m_location; }; +/// Utility class that creates an error and throws an exception if the +/// recursion depth is too deep. +class Parser::RecursionGuard +{ +public: + RecursionGuard(Parser& _parser): + m_parser(_parser) + { + m_parser.increaseRecursionDepth(); + } + ~RecursionGuard() + { + m_parser.decreaseRecursionDepth(); + } + +private: + Parser& m_parser; +}; + ASTPointer Parser::parse(shared_ptr const& _scanner) { try { + m_recursionDepth = 0; m_scanner = _scanner; ASTNodeFactory nodeFactory(*this); vector> nodes; @@ -90,6 +110,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); } } + solAssert(m_recursionDepth == 0, ""); return nodeFactory.createNode(nodes); } catch (FatalError const&) @@ -102,6 +123,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) ASTPointer Parser::parsePragmaDirective() { + RecursionGuard recursionGuard(*this); // pragma anything* ; // Currently supported: // pragma solidity ^0.4.0 || ^0.3.0; @@ -132,6 +154,7 @@ ASTPointer Parser::parsePragmaDirective() ASTPointer Parser::parseImportDirective() { + RecursionGuard recursionGuard(*this); // import "abc" [as x]; // import * as x from "abc"; // import {a as b, c} from "abc"; @@ -212,6 +235,7 @@ ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") @@ -275,6 +299,7 @@ ASTPointer Parser::parseContractDefinition(Token::Value _exp ASTPointer Parser::parseInheritanceSpecifier() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseUserDefinedTypeName()); vector> arguments; @@ -322,6 +347,7 @@ StateMutability Parser::parseStateMutability(Token::Value _token) Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { + RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; expectToken(Token::Function); if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) @@ -391,6 +417,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") @@ -449,6 +476,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A ASTPointer Parser::parseStructDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Struct); ASTPointer name = expectIdentifierToken(); @@ -466,6 +494,7 @@ ASTPointer Parser::parseStructDefinition() ASTPointer Parser::parseEnumValue() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); @@ -473,6 +502,7 @@ ASTPointer Parser::parseEnumValue() ASTPointer Parser::parseEnumDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Enum); ASTPointer name = expectIdentifierToken(); @@ -501,6 +531,7 @@ ASTPointer Parser::parseVariableDeclaration( ASTPointer const& _lookAheadArrayType ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ? ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); ASTPointer type; @@ -593,6 +624,7 @@ ASTPointer Parser::parseVariableDeclaration( ASTPointer Parser::parseModifierDefinition() { + RecursionGuard recursionGuard(*this); ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; }); m_insideModifier = true; @@ -620,6 +652,7 @@ ASTPointer Parser::parseModifierDefinition() ASTPointer Parser::parseEventDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") @@ -649,6 +682,7 @@ ASTPointer Parser::parseEventDefinition() ASTPointer Parser::parseUsingDirective() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); @@ -666,6 +700,7 @@ ASTPointer Parser::parseUsingDirective() ASTPointer Parser::parseModifierInvocation() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); vector> arguments; @@ -683,6 +718,7 @@ ASTPointer Parser::parseModifierInvocation() ASTPointer Parser::parseIdentifier() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); @@ -690,6 +726,7 @@ ASTPointer Parser::parseIdentifier() ASTPointer Parser::parseUserDefinedTypeName() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); vector identifierPath{*expectIdentifierToken()}; @@ -704,6 +741,7 @@ ASTPointer Parser::parseUserDefinedTypeName() ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory) { + RecursionGuard recursionGuard(*this); while (m_scanner->currentToken() == Token::LBrack) { m_scanner->next(); @@ -719,6 +757,7 @@ ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTN ASTPointer Parser::parseTypeName(bool _allowVar) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer type; Token::Value token = m_scanner->currentToken(); @@ -754,6 +793,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) ASTPointer Parser::parseFunctionType() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); FunctionHeaderParserResult header = parseFunctionHeader(true, false); return nodeFactory.createNode( @@ -766,6 +806,7 @@ ASTPointer Parser::parseFunctionType() ASTPointer Parser::parseMapping() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Mapping); expectToken(Token::LParen); @@ -792,6 +833,7 @@ ASTPointer Parser::parseParameterList( bool _allowEmpty ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); vector> parameters; VarDeclParserOptions options(_options); @@ -813,6 +855,7 @@ ASTPointer Parser::parseParameterList( ASTPointer Parser::parseBlock(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::LBrace); vector> statements; @@ -825,6 +868,7 @@ ASTPointer Parser::parseBlock(ASTPointer const& _docString) ASTPointer Parser::parseStatement() { + RecursionGuard recursionGuard(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared(m_scanner->currentCommentLiteral()); @@ -887,6 +931,7 @@ ASTPointer Parser::parseStatement() ASTPointer Parser::parseInlineAssembly(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Assembly); if (m_scanner->currentToken() == Token::StringLiteral) @@ -904,6 +949,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con ASTPointer Parser::parseIfStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::If); expectToken(Token::LParen); @@ -924,6 +970,7 @@ ASTPointer Parser::parseIfStatement(ASTPointer const& _d ASTPointer Parser::parseWhileStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::While); expectToken(Token::LParen); @@ -936,6 +983,7 @@ ASTPointer Parser::parseWhileStatement(ASTPointer con ASTPointer Parser::parseDoWhileStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Do); ASTPointer body = parseStatement(); @@ -951,6 +999,7 @@ ASTPointer Parser::parseDoWhileStatement(ASTPointer c ASTPointer Parser::parseForStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer initExpression; ASTPointer conditionExpression; @@ -984,6 +1033,7 @@ ASTPointer Parser::parseForStatement(ASTPointer const& ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); // These two cases are very hard to distinguish: // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9; // In the first case, x is a type name, in the second it is the name of a variable. @@ -1046,6 +1096,7 @@ ASTPointer Parser::parseVariableDeclarationStateme ASTPointer const& _lookAheadArrayType ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); if (_lookAheadArrayType) nodeFactory.setLocation(_lookAheadArrayType->location()); @@ -1109,6 +1160,7 @@ ASTPointer Parser::parseExpressionStatement( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseExpression(_lookAheadIndexAccessStructure); return ASTNodeFactory(*this, expression).createNode(_docString, expression); } @@ -1117,6 +1169,7 @@ ASTPointer Parser::parseExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure); if (Token::isAssignmentOp(m_scanner->currentToken())) { @@ -1145,6 +1198,7 @@ ASTPointer Parser::parseBinaryExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseUnaryExpression(_lookAheadIndexAccessStructure); ASTNodeFactory nodeFactory(*this, expression); int precedence = Token::precedence(m_scanner->currentToken()); @@ -1164,6 +1218,7 @@ ASTPointer Parser::parseUnaryExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); Token::Value token = m_scanner->currentToken(); @@ -1192,6 +1247,7 @@ ASTPointer Parser::parseLeftHandSideExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); @@ -1249,6 +1305,7 @@ ASTPointer Parser::parseLeftHandSideExpression( ASTPointer Parser::parsePrimaryExpression() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->currentToken(); ASTPointer expression; @@ -1339,6 +1396,7 @@ ASTPointer Parser::parsePrimaryExpression() vector> Parser::parseFunctionCallListArguments() { + RecursionGuard recursionGuard(*this); vector> arguments; if (m_scanner->currentToken() != Token::RParen) { @@ -1354,6 +1412,7 @@ vector> Parser::parseFunctionCallListArguments() pair>, vector>> Parser::parseFunctionCallArguments() { + RecursionGuard recursionGuard(*this); pair>, vector>> ret; Token::Value token = m_scanner->currentToken(); if (token == Token::LBrace) @@ -1420,6 +1479,7 @@ ASTPointer Parser::typeNameIndexAccessStructure( ) { solAssert(!_path.empty(), ""); + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); SourceLocation location = _path.front()->location(); location.end = _path.back()->location().end; @@ -1452,6 +1512,7 @@ ASTPointer Parser::expressionFromIndexAccessStructure( ) { solAssert(!_path.empty(), ""); + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this, _path.front()); ASTPointer expression(_path.front()); for (size_t i = 1; i < _path.size(); ++i) @@ -1475,11 +1536,25 @@ ASTPointer Parser::expressionFromIndexAccessStructure( ASTPointer Parser::createEmptyParameterList() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); return nodeFactory.createNode(vector>()); } +void Parser::increaseRecursionDepth() +{ + m_recursionDepth++; + if (m_recursionDepth >= 4096) + fatalParserError("Maximum recursion depth reached during parsing."); +} + +void Parser::decreaseRecursionDepth() +{ + solAssert(m_recursionDepth > 0, ""); + m_recursionDepth--; +} + string Parser::currentTokenName() { Token::Value token = m_scanner->currentToken(); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 97e60baa1715..5e6f3ef6b7ab 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -41,6 +41,7 @@ class Parser: public ParserBase private: class ASTNodeFactory; + class RecursionGuard; struct VarDeclParserOptions { @@ -164,8 +165,14 @@ class Parser: public ParserBase /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); + /// Increases the recursion depth and throws an exception if it is too deep. + void increaseRecursionDepth(); + void decreaseRecursionDepth(); + /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier. bool m_insideModifier = false; + /// Current recursion depth during parsing. + size_t m_recursionDepth = 0; }; } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 23276fcde478..30dc80d95d61 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1387,6 +1387,18 @@ BOOST_AUTO_TEST_CASE(recursion_depth3) CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); } +BOOST_AUTO_TEST_CASE(recursion_depth4) +{ + string text("contract C { function f() { uint a;"); + for (size_t i = 0; i < 30000; i++) + text += "("; + text += "a"; + for (size_t i = 0; i < 30000; i++) + text += "++)"; + text += "}}"; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) { char const* text = R"( From 12f3257e7d0e23502eb01a28c857ed56d9d2dbdc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 17 Jul 2017 11:38:29 +0200 Subject: [PATCH 072/163] Add statemutability field to the ABI --- Changelog.md | 1 + docs/abi-spec.rst | 3 ++- libsolidity/interface/ABI.cpp | 3 +++ test/libsolidity/SolidityABIJSON.cpp | 23 +++++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 256198f9036d..9bc0b354e336 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.16 (unreleased) Features: + * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. * Parser: Display previous visibility specifier in error if multiple are found. * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. * Static Analyzer: Warn about large storage structures. diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 2cf57427b3f9..7a1a9af0dd69 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -294,7 +294,8 @@ The JSON format for a contract's interface is given by an array of function and/ * `type`: the canonical type of the parameter. - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; - `constant`: `true` if function is :ref:`specified to not modify blockchain state `); -- `payable`: `true` if function accepts ether, defaults to `false`. +- `payable`: `true` if function accepts ether, defaults to `false`; +- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). `type` can be omitted, defaulting to `"function"`. diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 12f958fcc776..9fc2f4e82a77 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -38,6 +38,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) method["name"] = it.second->declaration().name(); method["constant"] = it.second->isConstant(); method["payable"] = it.second->isPayable(); + method["statemutability"] = stateMutabilityToString(it.second->stateMutability()); method["inputs"] = formatTypeList( externalFunctionType->parameterNames(), externalFunctionType->parameterTypes(), @@ -57,6 +58,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); solAssert(!!externalFunction, ""); method["payable"] = externalFunction->isPayable(); + method["statemutability"] = stateMutabilityToString(externalFunction->stateMutability()); method["inputs"] = formatTypeList( externalFunction->parameterNames(), externalFunction->parameterTypes(), @@ -71,6 +73,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value method; method["type"] = "fallback"; method["payable"] = externalFunctionType->isPayable(); + method["statemutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); abi.append(method); } for (auto const& it: _contractDef.interfaceEvents()) diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 452a26625fce..b06dcfe530a0 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -76,6 +76,7 @@ BOOST_AUTO_TEST_CASE(basic_test) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -119,6 +120,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -137,6 +139,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) "name": "g", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -169,6 +172,7 @@ BOOST_AUTO_TEST_CASE(multiple_params) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -207,6 +211,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) "name": "c", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -225,6 +230,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -258,6 +264,7 @@ BOOST_AUTO_TEST_CASE(const_function) "name": "foo", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -280,6 +287,7 @@ BOOST_AUTO_TEST_CASE(const_function) "name": "boo", "constant": true, "payable" : false, + "statemutability": "view", "type": "function", "inputs": [{ "name": "a", @@ -311,6 +319,7 @@ BOOST_AUTO_TEST_CASE(events) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -392,6 +401,7 @@ BOOST_AUTO_TEST_CASE(inherited) "name": "baseFunction", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [{ @@ -408,6 +418,7 @@ BOOST_AUTO_TEST_CASE(inherited) "name": "derivedFunction", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [{ @@ -463,6 +474,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -505,6 +517,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) "name": "f", "constant": false, "payable" : false, + "statemutability": "nonpayable", "type": "function", "inputs": [ { @@ -548,6 +561,7 @@ BOOST_AUTO_TEST_CASE(constructor_abi) } ], "payable": false, + "statemutability": "nonpayable", "type": "constructor" } ])"; @@ -574,6 +588,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) { "constant" : false, "payable" : false, + "statemutability": "nonpayable", "inputs" : [], "name" : "ret", "outputs" : [ @@ -592,6 +607,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) } ], "payable": false, + "statemutability": "nonpayable", "type": "constructor" } ] @@ -613,6 +629,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays) { "constant" : false, "payable" : false, + "statemutability": "nonpayable", "name": "f", "inputs": [ { "name": "a", "type": "string" }, @@ -641,6 +658,7 @@ BOOST_AUTO_TEST_CASE(library_function) { "constant" : false, "payable" : false, + "statemutability": "nonpayable", "name": "f", "inputs": [ { "name": "b", "type": "test.StructType storage" }, @@ -670,6 +688,7 @@ BOOST_AUTO_TEST_CASE(include_fallback_function) [ { "payable": false, + "statemutability": "nonpayable", "type" : "fallback" } ] @@ -691,6 +710,7 @@ BOOST_AUTO_TEST_CASE(payable_function) { "constant" : false, "payable": false, + "statemutability": "nonpayable", "inputs": [], "name": "f", "outputs": [], @@ -699,6 +719,7 @@ BOOST_AUTO_TEST_CASE(payable_function) { "constant" : false, "payable": true, + "statemutability": "payable", "inputs": [], "name": "g", "outputs": [], @@ -721,6 +742,7 @@ BOOST_AUTO_TEST_CASE(payable_fallback_function) [ { "payable": true, + "statemutability": "payable", "type" : "fallback" } ] @@ -741,6 +763,7 @@ BOOST_AUTO_TEST_CASE(function_type) { "constant" : false, "payable": false, + "statemutability": "nonpayable", "inputs": [{ "name": "x", "type": "function" From 1f5ab603a74ded4058d3c7103e436112852b45ba Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 01:40:53 +0100 Subject: [PATCH 073/163] Add test for payable constructor in ABI --- test/libsolidity/SolidityABIJSON.cpp | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index b06dcfe530a0..80b4b6ad2012 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -568,6 +568,37 @@ BOOST_AUTO_TEST_CASE(constructor_abi) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(payable_constructor_abi) +{ + char const* sourceCode = R"( + contract test { + function test(uint param1, test param2, bool param3) payable {} + } + )"; + + char const* interface = R"([ + { + "inputs": [ + { + "name": "param1", + "type": "uint256" + }, + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bool" + } + ], + "payable": true, + "statemutability": "payable", + "type": "constructor" + } + ])"; + checkInterface(sourceCode, interface); +} BOOST_AUTO_TEST_CASE(return_param_in_abi) { From 7222fac45680dd9955fd37c6245760014a19873a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 11:28:04 +0100 Subject: [PATCH 074/163] Remove DocumentationType from natspec --- libsolidity/interface/CompilerStack.cpp | 53 ++++++++++++---------- libsolidity/interface/CompilerStack.h | 19 ++++---- libsolidity/interface/StandardCompiler.cpp | 4 +- solc/CommandLineInterface.cpp | 35 +++++++------- solc/CommandLineInterface.h | 2 +- test/libsolidity/SolidityNatspecJSON.cpp | 4 +- 6 files changed, 59 insertions(+), 58 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 70bebfa53e2c..07596b99c684 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -406,39 +406,42 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const return *_contract.abi; } -Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const +Json::Value const& CompilerStack::natspecUser(string const& _contractName) const { - return natspec(contract(_contractName), _type); + return natspecUser(contract(_contractName)); } -Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const +Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const { if (m_stackState < AnalysisSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); solAssert(_contract.contract, ""); - std::unique_ptr* doc; - // checks wheather we already have the documentation - switch (_type) - { - case DocumentationType::NatspecUser: - doc = &_contract.userDocumentation; - // caches the result - if (!*doc) - doc->reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); - break; - case DocumentationType::NatspecDev: - doc = &_contract.devDocumentation; - // caches the result - if (!*doc) - doc->reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); - break; - default: - solAssert(false, "Illegal documentation type."); - } + // caches the result + if (!_contract.userDocumentation) + _contract.userDocumentation.reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); + + return *_contract.userDocumentation; +} + +Json::Value const& CompilerStack::natspecDev(string const& _contractName) const +{ + return natspecDev(contract(_contractName)); +} + +Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const +{ + if (m_stackState < AnalysisSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.devDocumentation) + _contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); - return *(*doc); + return *_contract.devDocumentation; } Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const @@ -819,8 +822,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); meta["output"]["abi"] = contractABI(_contract); - meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser); - meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev); + meta["output"]["userdoc"] = natspecUser(_contract); + meta["output"]["devdoc"] = natspecDev(_contract); return jsonCompactPrint(meta); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index d287f22431ab..54e3e23f89a6 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -63,12 +63,6 @@ class Natspec; class Error; class DeclarationContainer; -enum class DocumentationType: uint8_t -{ - NatspecUser = 1, - NatspecDev -}; - /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. * It holds state and can be used to either step through the compilation stages (and abort e.g. @@ -203,11 +197,13 @@ class CompilerStack: boost::noncopyable /// Prerequisite: Successful call to parse or compile. Json::Value const& contractABI(std::string const& _contractName = "") const; - /// @returns a JSON representing the contract's documentation. + /// @returns a JSON representing the contract's user documentation. + /// Prerequisite: Successful call to parse or compile. + Json::Value const& natspecUser(std::string const& _contractName) const; + + /// @returns a JSON representing the contract's developer documentation. /// Prerequisite: Successful call to parse or compile. - /// @param type The type of the documentation to get. - /// Can be one of 4 types defined at @c DocumentationType - Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const; + Json::Value const& natspecDev(std::string const& _contractName) const; /// @returns a JSON representing a map of method identifiers (hashes) to function names. Json::Value methodIdentifiers(std::string const& _contractName) const; @@ -274,7 +270,8 @@ class CompilerStack: boost::noncopyable std::string createMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; Json::Value const& contractABI(Contract const&) const; - Json::Value const& natspec(Contract const&, DocumentationType _type) const; + Json::Value const& natspecUser(Contract const&) const; + Json::Value const& natspecDev(Contract const&) const; /// @returns the offset of the entry point of the given function into the list of assembly items /// or zero if it is not found or does not exist. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index dd135ce57f4c..7a6f99893edc 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -394,8 +394,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value contractData(Json::objectValue); contractData["abi"] = m_compilerStack.contractABI(contractName); contractData["metadata"] = m_compilerStack.metadata(contractName); - contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser); - contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev); + contractData["userdoc"] = m_compilerStack.natspecUser(contractName); + contractData["devdoc"] = m_compilerStack.natspecDev(contractName); // EVM Json::Value evmData(Json::objectValue); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 740061a18255..152526f4efbe 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -316,31 +316,32 @@ void CommandLineInterface::handleABI(string const& _contract) cout << "Contract JSON ABI " << endl << data << endl; } -void CommandLineInterface::handleNatspec(DocumentationType _type, string const& _contract) +void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { std::string argName; std::string suffix; std::string title; - switch(_type) + + if (_natspecDev) { - case DocumentationType::NatspecUser: - argName = g_argNatspecUser; - suffix = ".docuser"; - title = "User Documentation"; - break; - case DocumentationType::NatspecDev: argName = g_argNatspecDev; suffix = ".docdev"; title = "Developer Documentation"; - break; - default: - // should never happen - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type")); + } + else + { + argName = g_argNatspecUser; + suffix = ".docuser"; + title = "User Documentation"; } if (m_args.count(argName)) { - std::string output = dev::jsonPrettyPrint(m_compiler->natspec(_contract, _type)); + std::string output = dev::jsonPrettyPrint( + _natspecDev ? + m_compiler->natspecDev(_contract) : + m_compiler->natspecUser(_contract) + ); if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + suffix, output); @@ -881,9 +882,9 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strSignatureHashes)) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); if (requests.count(g_strNatspecDev)) - contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecDev)); + contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspecDev(contractName)); if (requests.count(g_strNatspecUser)) - contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspec(contractName, DocumentationType::NatspecUser)); + contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspecUser(contractName)); output[g_strContracts][contractName] = contractData; } @@ -1170,8 +1171,8 @@ void CommandLineInterface::outputCompilationResults() handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); - handleNatspec(DocumentationType::NatspecDev, contract); - handleNatspec(DocumentationType::NatspecUser, contract); + handleNatspec(true, contract); + handleNatspec(false, contract); } // end of contracts iteration if (m_args.count(g_argFormal)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 8a476ef59730..bf9400e4ce23 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -66,7 +66,7 @@ class CommandLineInterface void handleSignatureHashes(std::string const& _contract); void handleMetadata(std::string const& _contract); void handleABI(std::string const& _contract); - void handleNatspec(DocumentationType _type, std::string const& _contract); + void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index be20a9f2183d..149221d5abe4 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -51,9 +51,9 @@ class DocumentationChecker Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecUser); + generatedDocumentation = m_compilerStack.natspecUser(""); else - generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecDev); + generatedDocumentation = m_compilerStack.natspecDev(""); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( From 2dc9e53a4987f8b7c5ed6dd33e71a0185e41d5d7 Mon Sep 17 00:00:00 2001 From: Isaac Ibiapina Date: Tue, 15 Aug 2017 14:57:10 -0400 Subject: [PATCH 075/163] Specify address as string on events documentation --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 9367169173b6..5683ba775b26 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -626,7 +626,7 @@ The use in the JavaScript API would be as follows: var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); - var clientReceipt = ClientReceipt.at(0x123 /* address */); + var clientReceipt = ClientReceipt.at("0x123" /* string of address */); var event = clientReceipt.Deposit(); From 2021508653bd6b8159685060d6830cdf3592f8a3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Aug 2017 11:31:50 +0200 Subject: [PATCH 076/163] Update contracts.rst --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 5683ba775b26..7b972e17bed3 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -626,7 +626,7 @@ The use in the JavaScript API would be as follows: var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); - var clientReceipt = ClientReceipt.at("0x123" /* string of address */); + var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */); var event = clientReceipt.Deposit(); From 3d595d4b149e590138f35a6f535c0a20a28e6f04 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Aug 2017 13:52:06 +0200 Subject: [PATCH 077/163] Warn about shift of literals. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 5 ++- .../SolidityNameAndTypeResolution.cpp | 38 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9bc0b354e336..7d026862e832 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: * Static Analyzer: Warn about large storage structures. * Metadata: Store experimental flag in metadata CBOR. * Type Checker: More detailed error message for invalid overrides. + * Type Checker: Warn about shifting a literal. Bugfixes: * Parser: Enforce commas between array and tuple elements. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index dbc95c4f65b7..ca848dd0fc7f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1305,8 +1305,9 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) _operation.leftExpression().annotation().isPure && _operation.rightExpression().annotation().isPure; - if (_operation.getOperator() == Token::Exp) + if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) { + string operation = _operation.getOperator() == Token::Exp ? "exponentiation" : "shift"; if ( leftType->category() == Type::Category::RationalNumber && rightType->category() != Type::Category::RationalNumber @@ -1320,7 +1321,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) )) m_errorReporter.warning( _operation.location(), - "Result of exponentiation has type " + commonType->toString() + " and thus " + "Result of " + operation + " has type " + commonType->toString() + " and thus " "might overflow. Silence this warning by converting the literal to the " "expected type." ); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 51d60596441e..1c83e1a3d0fb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1765,6 +1765,44 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); } +BOOST_AUTO_TEST_CASE(shift_warn_literal_base) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint) { + uint8 x = 100; + return 10 << x; + } + } + )"; + CHECK_WARNING(sourceCode, "might overflow"); + sourceCode = R"( + contract test { + function f() returns(uint) { + uint8 x = 100; + return uint8(10) << x; + } + } + )"; + CHECK_SUCCESS(sourceCode); + sourceCode = R"( + contract test { + function f() returns(uint) { + return 2 << 80; + } + } + )"; + CHECK_SUCCESS(sourceCode); + sourceCode = R"( + contract test { + function f() returns(uint) { + uint8 x = 100; + return 10 >> x; + } + } + )"; + CHECK_SUCCESS(sourceCode); +} BOOST_AUTO_TEST_CASE(warn_var_from_zero) { From 80ee7c2b362c6b559d821723855eeb82c8b5f5a8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 14:30:56 +0100 Subject: [PATCH 078/163] Fix ABI for fixed types --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 7a1a9af0dd69..a9110a0af705 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -44,7 +44,7 @@ The following elementary types exist: - `bool`: equivalent to `uint8` restricted to the values 0 and 1 -- `fixedx`: signed fixed-point decimal number of `M` bits, `0 < M <= 256`, `M % 8 ==0`, and `0 < N <= 80`, which denotes the value `v` as `v / (10 ** N)`. +- `fixedx`: signed fixed-point decimal number of `M` bits, `8 <= M <= 256`, `M % 8 ==0`, and `0 < N <= 80`, which denotes the value `v` as `v / (10 ** N)`. - `ufixedx`: unsigned variant of `fixedx`. From 4acc552e66204bf11aa274c97c973c856e87f8cd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 14:36:56 +0100 Subject: [PATCH 079/163] Fix the grammar for fixed point types --- docs/grammar.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 6c0414608016..a9838ccbc56e 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -142,9 +142,9 @@ Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' -Fixed = 'fixed' | 'fixed0x8' | 'fixed0x16' | 'fixed0x24' | 'fixed0x32' | 'fixed0x40' | 'fixed0x48' | 'fixed0x56' | 'fixed0x64' | 'fixed0x72' | 'fixed0x80' | 'fixed0x88' | 'fixed0x96' | 'fixed0x104' | 'fixed0x112' | 'fixed0x120' | 'fixed0x128' | 'fixed0x136' | 'fixed0x144' | 'fixed0x152' | 'fixed0x160' | 'fixed0x168' | 'fixed0x176' | 'fixed0x184' | 'fixed0x192' | 'fixed0x200' | 'fixed0x208' | 'fixed0x216' | 'fixed0x224' | 'fixed0x232' | 'fixed0x240' | 'fixed0x248' | 'fixed0x256' | 'fixed8x8' | 'fixed8x16' | 'fixed8x24' | 'fixed8x32' | 'fixed8x40' | 'fixed8x48' | 'fixed8x56' | 'fixed8x64' | 'fixed8x72' | 'fixed8x80' | 'fixed8x88' | 'fixed8x96' | 'fixed8x104' | 'fixed8x112' | 'fixed8x120' | 'fixed8x128' | 'fixed8x136' | 'fixed8x144' | 'fixed8x152' | 'fixed8x160' | 'fixed8x168' | 'fixed8x176' | 'fixed8x184' | 'fixed8x192' | 'fixed8x200' | 'fixed8x208' | 'fixed8x216' | 'fixed8x224' | 'fixed8x232' | 'fixed8x240' | 'fixed8x248' | 'fixed16x8' | 'fixed16x16' | 'fixed16x24' | 'fixed16x32' | 'fixed16x40' | 'fixed16x48' | 'fixed16x56' | 'fixed16x64' | 'fixed16x72' | 'fixed16x80' | 'fixed16x88' | 'fixed16x96' | 'fixed16x104' | 'fixed16x112' | 'fixed16x120' | 'fixed16x128' | 'fixed16x136' | 'fixed16x144' | 'fixed16x152' | 'fixed16x160' | 'fixed16x168' | 'fixed16x176' | 'fixed16x184' | 'fixed16x192' | 'fixed16x200' | 'fixed16x208' | 'fixed16x216' | 'fixed16x224' | 'fixed16x232' | 'fixed16x240' | 'fixed24x8' | 'fixed24x16' | 'fixed24x24' | 'fixed24x32' | 'fixed24x40' | 'fixed24x48' | 'fixed24x56' | 'fixed24x64' | 'fixed24x72' | 'fixed24x80' | 'fixed24x88' | 'fixed24x96' | 'fixed24x104' | 'fixed24x112' | 'fixed24x120' | 'fixed24x128' | 'fixed24x136' | 'fixed24x144' | 'fixed24x152' | 'fixed24x160' | 'fixed24x168' | 'fixed24x176' | 'fixed24x184' | 'fixed24x192' | 'fixed24x200' | 'fixed24x208' | 'fixed24x216' | 'fixed24x224' | 'fixed24x232' | 'fixed32x8' | 'fixed32x16' | 'fixed32x24' | 'fixed32x32' | 'fixed32x40' | 'fixed32x48' | 'fixed32x56' | 'fixed32x64' | 'fixed32x72' | 'fixed32x80' | 'fixed32x88' | 'fixed32x96' | 'fixed32x104' | 'fixed32x112' | 'fixed32x120' | 'fixed32x128' | 'fixed32x136' | 'fixed32x144' | 'fixed32x152' | 'fixed32x160' | 'fixed32x168' | 'fixed32x176' | 'fixed32x184' | 'fixed32x192' | 'fixed32x200' | 'fixed32x208' | 'fixed32x216' | 'fixed32x224' | 'fixed40x8' | 'fixed40x16' | 'fixed40x24' | 'fixed40x32' | 'fixed40x40' | 'fixed40x48' | 'fixed40x56' | 'fixed40x64' | 'fixed40x72' | 'fixed40x80' | 'fixed40x88' | 'fixed40x96' | 'fixed40x104' | 'fixed40x112' | 'fixed40x120' | 'fixed40x128' | 'fixed40x136' | 'fixed40x144' | 'fixed40x152' | 'fixed40x160' | 'fixed40x168' | 'fixed40x176' | 'fixed40x184' | 'fixed40x192' | 'fixed40x200' | 'fixed40x208' | 'fixed40x216' | 'fixed48x8' | 'fixed48x16' | 'fixed48x24' | 'fixed48x32' | 'fixed48x40' | 'fixed48x48' | 'fixed48x56' | 'fixed48x64' | 'fixed48x72' | 'fixed48x80' | 'fixed48x88' | 'fixed48x96' | 'fixed48x104' | 'fixed48x112' | 'fixed48x120' | 'fixed48x128' | 'fixed48x136' | 'fixed48x144' | 'fixed48x152' | 'fixed48x160' | 'fixed48x168' | 'fixed48x176' | 'fixed48x184' | 'fixed48x192' | 'fixed48x200' | 'fixed48x208' | 'fixed56x8' | 'fixed56x16' | 'fixed56x24' | 'fixed56x32' | 'fixed56x40' | 'fixed56x48' | 'fixed56x56' | 'fixed56x64' | 'fixed56x72' | 'fixed56x80' | 'fixed56x88' | 'fixed56x96' | 'fixed56x104' | 'fixed56x112' | 'fixed56x120' | 'fixed56x128' | 'fixed56x136' | 'fixed56x144' | 'fixed56x152' | 'fixed56x160' | 'fixed56x168' | 'fixed56x176' | 'fixed56x184' | 'fixed56x192' | 'fixed56x200' | 'fixed64x8' | 'fixed64x16' | 'fixed64x24' | 'fixed64x32' | 'fixed64x40' | 'fixed64x48' | 'fixed64x56' | 'fixed64x64' | 'fixed64x72' | 'fixed64x80' | 'fixed64x88' | 'fixed64x96' | 'fixed64x104' | 'fixed64x112' | 'fixed64x120' | 'fixed64x128' | 'fixed64x136' | 'fixed64x144' | 'fixed64x152' | 'fixed64x160' | 'fixed64x168' | 'fixed64x176' | 'fixed64x184' | 'fixed64x192' | 'fixed72x8' | 'fixed72x16' | 'fixed72x24' | 'fixed72x32' | 'fixed72x40' | 'fixed72x48' | 'fixed72x56' | 'fixed72x64' | 'fixed72x72' | 'fixed72x80' | 'fixed72x88' | 'fixed72x96' | 'fixed72x104' | 'fixed72x112' | 'fixed72x120' | 'fixed72x128' | 'fixed72x136' | 'fixed72x144' | 'fixed72x152' | 'fixed72x160' | 'fixed72x168' | 'fixed72x176' | 'fixed72x184' | 'fixed80x8' | 'fixed80x16' | 'fixed80x24' | 'fixed80x32' | 'fixed80x40' | 'fixed80x48' | 'fixed80x56' | 'fixed80x64' | 'fixed80x72' | 'fixed80x80' | 'fixed80x88' | 'fixed80x96' | 'fixed80x104' | 'fixed80x112' | 'fixed80x120' | 'fixed80x128' | 'fixed80x136' | 'fixed80x144' | 'fixed80x152' | 'fixed80x160' | 'fixed80x168' | 'fixed80x176' | 'fixed88x8' | 'fixed88x16' | 'fixed88x24' | 'fixed88x32' | 'fixed88x40' | 'fixed88x48' | 'fixed88x56' | 'fixed88x64' | 'fixed88x72' | 'fixed88x80' | 'fixed88x88' | 'fixed88x96' | 'fixed88x104' | 'fixed88x112' | 'fixed88x120' | 'fixed88x128' | 'fixed88x136' | 'fixed88x144' | 'fixed88x152' | 'fixed88x160' | 'fixed88x168' | 'fixed96x8' | 'fixed96x16' | 'fixed96x24' | 'fixed96x32' | 'fixed96x40' | 'fixed96x48' | 'fixed96x56' | 'fixed96x64' | 'fixed96x72' | 'fixed96x80' | 'fixed96x88' | 'fixed96x96' | 'fixed96x104' | 'fixed96x112' | 'fixed96x120' | 'fixed96x128' | 'fixed96x136' | 'fixed96x144' | 'fixed96x152' | 'fixed96x160' | 'fixed104x8' | 'fixed104x16' | 'fixed104x24' | 'fixed104x32' | 'fixed104x40' | 'fixed104x48' | 'fixed104x56' | 'fixed104x64' | 'fixed104x72' | 'fixed104x80' | 'fixed104x88' | 'fixed104x96' | 'fixed104x104' | 'fixed104x112' | 'fixed104x120' | 'fixed104x128' | 'fixed104x136' | 'fixed104x144' | 'fixed104x152' | 'fixed112x8' | 'fixed112x16' | 'fixed112x24' | 'fixed112x32' | 'fixed112x40' | 'fixed112x48' | 'fixed112x56' | 'fixed112x64' | 'fixed112x72' | 'fixed112x80' | 'fixed112x88' | 'fixed112x96' | 'fixed112x104' | 'fixed112x112' | 'fixed112x120' | 'fixed112x128' | 'fixed112x136' | 'fixed112x144' | 'fixed120x8' | 'fixed120x16' | 'fixed120x24' | 'fixed120x32' | 'fixed120x40' | 'fixed120x48' | 'fixed120x56' | 'fixed120x64' | 'fixed120x72' | 'fixed120x80' | 'fixed120x88' | 'fixed120x96' | 'fixed120x104' | 'fixed120x112' | 'fixed120x120' | 'fixed120x128' | 'fixed120x136' | 'fixed128x8' | 'fixed128x16' | 'fixed128x24' | 'fixed128x32' | 'fixed128x40' | 'fixed128x48' | 'fixed128x56' | 'fixed128x64' | 'fixed128x72' | 'fixed128x80' | 'fixed128x88' | 'fixed128x96' | 'fixed128x104' | 'fixed128x112' | 'fixed128x120' | 'fixed128x128' | 'fixed136x8' | 'fixed136x16' | 'fixed136x24' | 'fixed136x32' | 'fixed136x40' | 'fixed136x48' | 'fixed136x56' | 'fixed136x64' | 'fixed136x72' | 'fixed136x80' | 'fixed136x88' | 'fixed136x96' | 'fixed136x104' | 'fixed136x112' | 'fixed136x120' | 'fixed144x8' | 'fixed144x16' | 'fixed144x24' | 'fixed144x32' | 'fixed144x40' | 'fixed144x48' | 'fixed144x56' | 'fixed144x64' | 'fixed144x72' | 'fixed144x80' | 'fixed144x88' | 'fixed144x96' | 'fixed144x104' | 'fixed144x112' | 'fixed152x8' | 'fixed152x16' | 'fixed152x24' | 'fixed152x32' | 'fixed152x40' | 'fixed152x48' | 'fixed152x56' | 'fixed152x64' | 'fixed152x72' | 'fixed152x80' | 'fixed152x88' | 'fixed152x96' | 'fixed152x104' | 'fixed160x8' | 'fixed160x16' | 'fixed160x24' | 'fixed160x32' | 'fixed160x40' | 'fixed160x48' | 'fixed160x56' | 'fixed160x64' | 'fixed160x72' | 'fixed160x80' | 'fixed160x88' | 'fixed160x96' | 'fixed168x8' | 'fixed168x16' | 'fixed168x24' | 'fixed168x32' | 'fixed168x40' | 'fixed168x48' | 'fixed168x56' | 'fixed168x64' | 'fixed168x72' | 'fixed168x80' | 'fixed168x88' | 'fixed176x8' | 'fixed176x16' | 'fixed176x24' | 'fixed176x32' | 'fixed176x40' | 'fixed176x48' | 'fixed176x56' | 'fixed176x64' | 'fixed176x72' | 'fixed176x80' | 'fixed184x8' | 'fixed184x16' | 'fixed184x24' | 'fixed184x32' | 'fixed184x40' | 'fixed184x48' | 'fixed184x56' | 'fixed184x64' | 'fixed184x72' | 'fixed192x8' | 'fixed192x16' | 'fixed192x24' | 'fixed192x32' | 'fixed192x40' | 'fixed192x48' | 'fixed192x56' | 'fixed192x64' | 'fixed200x8' | 'fixed200x16' | 'fixed200x24' | 'fixed200x32' | 'fixed200x40' | 'fixed200x48' | 'fixed200x56' | 'fixed208x8' | 'fixed208x16' | 'fixed208x24' | 'fixed208x32' | 'fixed208x40' | 'fixed208x48' | 'fixed216x8' | 'fixed216x16' | 'fixed216x24' | 'fixed216x32' | 'fixed216x40' | 'fixed224x8' | 'fixed224x16' | 'fixed224x24' | 'fixed224x32' | 'fixed232x8' | 'fixed232x16' | 'fixed232x24' | 'fixed240x8' | 'fixed240x16' | 'fixed248x8' +Fixed = 'fixed' | ( 'fixed' DecimalNumber 'x' DecimalNumber ) -Ufixed = 'ufixed' | 'ufixed0x8' | 'ufixed0x16' | 'ufixed0x24' | 'ufixed0x32' | 'ufixed0x40' | 'ufixed0x48' | 'ufixed0x56' | 'ufixed0x64' | 'ufixed0x72' | 'ufixed0x80' | 'ufixed0x88' | 'ufixed0x96' | 'ufixed0x104' | 'ufixed0x112' | 'ufixed0x120' | 'ufixed0x128' | 'ufixed0x136' | 'ufixed0x144' | 'ufixed0x152' | 'ufixed0x160' | 'ufixed0x168' | 'ufixed0x176' | 'ufixed0x184' | 'ufixed0x192' | 'ufixed0x200' | 'ufixed0x208' | 'ufixed0x216' | 'ufixed0x224' | 'ufixed0x232' | 'ufixed0x240' | 'ufixed0x248' | 'ufixed0x256' | 'ufixed8x8' | 'ufixed8x16' | 'ufixed8x24' | 'ufixed8x32' | 'ufixed8x40' | 'ufixed8x48' | 'ufixed8x56' | 'ufixed8x64' | 'ufixed8x72' | 'ufixed8x80' | 'ufixed8x88' | 'ufixed8x96' | 'ufixed8x104' | 'ufixed8x112' | 'ufixed8x120' | 'ufixed8x128' | 'ufixed8x136' | 'ufixed8x144' | 'ufixed8x152' | 'ufixed8x160' | 'ufixed8x168' | 'ufixed8x176' | 'ufixed8x184' | 'ufixed8x192' | 'ufixed8x200' | 'ufixed8x208' | 'ufixed8x216' | 'ufixed8x224' | 'ufixed8x232' | 'ufixed8x240' | 'ufixed8x248' | 'ufixed16x8' | 'ufixed16x16' | 'ufixed16x24' | 'ufixed16x32' | 'ufixed16x40' | 'ufixed16x48' | 'ufixed16x56' | 'ufixed16x64' | 'ufixed16x72' | 'ufixed16x80' | 'ufixed16x88' | 'ufixed16x96' | 'ufixed16x104' | 'ufixed16x112' | 'ufixed16x120' | 'ufixed16x128' | 'ufixed16x136' | 'ufixed16x144' | 'ufixed16x152' | 'ufixed16x160' | 'ufixed16x168' | 'ufixed16x176' | 'ufixed16x184' | 'ufixed16x192' | 'ufixed16x200' | 'ufixed16x208' | 'ufixed16x216' | 'ufixed16x224' | 'ufixed16x232' | 'ufixed16x240' | 'ufixed24x8' | 'ufixed24x16' | 'ufixed24x24' | 'ufixed24x32' | 'ufixed24x40' | 'ufixed24x48' | 'ufixed24x56' | 'ufixed24x64' | 'ufixed24x72' | 'ufixed24x80' | 'ufixed24x88' | 'ufixed24x96' | 'ufixed24x104' | 'ufixed24x112' | 'ufixed24x120' | 'ufixed24x128' | 'ufixed24x136' | 'ufixed24x144' | 'ufixed24x152' | 'ufixed24x160' | 'ufixed24x168' | 'ufixed24x176' | 'ufixed24x184' | 'ufixed24x192' | 'ufixed24x200' | 'ufixed24x208' | 'ufixed24x216' | 'ufixed24x224' | 'ufixed24x232' | 'ufixed32x8' | 'ufixed32x16' | 'ufixed32x24' | 'ufixed32x32' | 'ufixed32x40' | 'ufixed32x48' | 'ufixed32x56' | 'ufixed32x64' | 'ufixed32x72' | 'ufixed32x80' | 'ufixed32x88' | 'ufixed32x96' | 'ufixed32x104' | 'ufixed32x112' | 'ufixed32x120' | 'ufixed32x128' | 'ufixed32x136' | 'ufixed32x144' | 'ufixed32x152' | 'ufixed32x160' | 'ufixed32x168' | 'ufixed32x176' | 'ufixed32x184' | 'ufixed32x192' | 'ufixed32x200' | 'ufixed32x208' | 'ufixed32x216' | 'ufixed32x224' | 'ufixed40x8' | 'ufixed40x16' | 'ufixed40x24' | 'ufixed40x32' | 'ufixed40x40' | 'ufixed40x48' | 'ufixed40x56' | 'ufixed40x64' | 'ufixed40x72' | 'ufixed40x80' | 'ufixed40x88' | 'ufixed40x96' | 'ufixed40x104' | 'ufixed40x112' | 'ufixed40x120' | 'ufixed40x128' | 'ufixed40x136' | 'ufixed40x144' | 'ufixed40x152' | 'ufixed40x160' | 'ufixed40x168' | 'ufixed40x176' | 'ufixed40x184' | 'ufixed40x192' | 'ufixed40x200' | 'ufixed40x208' | 'ufixed40x216' | 'ufixed48x8' | 'ufixed48x16' | 'ufixed48x24' | 'ufixed48x32' | 'ufixed48x40' | 'ufixed48x48' | 'ufixed48x56' | 'ufixed48x64' | 'ufixed48x72' | 'ufixed48x80' | 'ufixed48x88' | 'ufixed48x96' | 'ufixed48x104' | 'ufixed48x112' | 'ufixed48x120' | 'ufixed48x128' | 'ufixed48x136' | 'ufixed48x144' | 'ufixed48x152' | 'ufixed48x160' | 'ufixed48x168' | 'ufixed48x176' | 'ufixed48x184' | 'ufixed48x192' | 'ufixed48x200' | 'ufixed48x208' | 'ufixed56x8' | 'ufixed56x16' | 'ufixed56x24' | 'ufixed56x32' | 'ufixed56x40' | 'ufixed56x48' | 'ufixed56x56' | 'ufixed56x64' | 'ufixed56x72' | 'ufixed56x80' | 'ufixed56x88' | 'ufixed56x96' | 'ufixed56x104' | 'ufixed56x112' | 'ufixed56x120' | 'ufixed56x128' | 'ufixed56x136' | 'ufixed56x144' | 'ufixed56x152' | 'ufixed56x160' | 'ufixed56x168' | 'ufixed56x176' | 'ufixed56x184' | 'ufixed56x192' | 'ufixed56x200' | 'ufixed64x8' | 'ufixed64x16' | 'ufixed64x24' | 'ufixed64x32' | 'ufixed64x40' | 'ufixed64x48' | 'ufixed64x56' | 'ufixed64x64' | 'ufixed64x72' | 'ufixed64x80' | 'ufixed64x88' | 'ufixed64x96' | 'ufixed64x104' | 'ufixed64x112' | 'ufixed64x120' | 'ufixed64x128' | 'ufixed64x136' | 'ufixed64x144' | 'ufixed64x152' | 'ufixed64x160' | 'ufixed64x168' | 'ufixed64x176' | 'ufixed64x184' | 'ufixed64x192' | 'ufixed72x8' | 'ufixed72x16' | 'ufixed72x24' | 'ufixed72x32' | 'ufixed72x40' | 'ufixed72x48' | 'ufixed72x56' | 'ufixed72x64' | 'ufixed72x72' | 'ufixed72x80' | 'ufixed72x88' | 'ufixed72x96' | 'ufixed72x104' | 'ufixed72x112' | 'ufixed72x120' | 'ufixed72x128' | 'ufixed72x136' | 'ufixed72x144' | 'ufixed72x152' | 'ufixed72x160' | 'ufixed72x168' | 'ufixed72x176' | 'ufixed72x184' | 'ufixed80x8' | 'ufixed80x16' | 'ufixed80x24' | 'ufixed80x32' | 'ufixed80x40' | 'ufixed80x48' | 'ufixed80x56' | 'ufixed80x64' | 'ufixed80x72' | 'ufixed80x80' | 'ufixed80x88' | 'ufixed80x96' | 'ufixed80x104' | 'ufixed80x112' | 'ufixed80x120' | 'ufixed80x128' | 'ufixed80x136' | 'ufixed80x144' | 'ufixed80x152' | 'ufixed80x160' | 'ufixed80x168' | 'ufixed80x176' | 'ufixed88x8' | 'ufixed88x16' | 'ufixed88x24' | 'ufixed88x32' | 'ufixed88x40' | 'ufixed88x48' | 'ufixed88x56' | 'ufixed88x64' | 'ufixed88x72' | 'ufixed88x80' | 'ufixed88x88' | 'ufixed88x96' | 'ufixed88x104' | 'ufixed88x112' | 'ufixed88x120' | 'ufixed88x128' | 'ufixed88x136' | 'ufixed88x144' | 'ufixed88x152' | 'ufixed88x160' | 'ufixed88x168' | 'ufixed96x8' | 'ufixed96x16' | 'ufixed96x24' | 'ufixed96x32' | 'ufixed96x40' | 'ufixed96x48' | 'ufixed96x56' | 'ufixed96x64' | 'ufixed96x72' | 'ufixed96x80' | 'ufixed96x88' | 'ufixed96x96' | 'ufixed96x104' | 'ufixed96x112' | 'ufixed96x120' | 'ufixed96x128' | 'ufixed96x136' | 'ufixed96x144' | 'ufixed96x152' | 'ufixed96x160' | 'ufixed104x8' | 'ufixed104x16' | 'ufixed104x24' | 'ufixed104x32' | 'ufixed104x40' | 'ufixed104x48' | 'ufixed104x56' | 'ufixed104x64' | 'ufixed104x72' | 'ufixed104x80' | 'ufixed104x88' | 'ufixed104x96' | 'ufixed104x104' | 'ufixed104x112' | 'ufixed104x120' | 'ufixed104x128' | 'ufixed104x136' | 'ufixed104x144' | 'ufixed104x152' | 'ufixed112x8' | 'ufixed112x16' | 'ufixed112x24' | 'ufixed112x32' | 'ufixed112x40' | 'ufixed112x48' | 'ufixed112x56' | 'ufixed112x64' | 'ufixed112x72' | 'ufixed112x80' | 'ufixed112x88' | 'ufixed112x96' | 'ufixed112x104' | 'ufixed112x112' | 'ufixed112x120' | 'ufixed112x128' | 'ufixed112x136' | 'ufixed112x144' | 'ufixed120x8' | 'ufixed120x16' | 'ufixed120x24' | 'ufixed120x32' | 'ufixed120x40' | 'ufixed120x48' | 'ufixed120x56' | 'ufixed120x64' | 'ufixed120x72' | 'ufixed120x80' | 'ufixed120x88' | 'ufixed120x96' | 'ufixed120x104' | 'ufixed120x112' | 'ufixed120x120' | 'ufixed120x128' | 'ufixed120x136' | 'ufixed128x8' | 'ufixed128x16' | 'ufixed128x24' | 'ufixed128x32' | 'ufixed128x40' | 'ufixed128x48' | 'ufixed128x56' | 'ufixed128x64' | 'ufixed128x72' | 'ufixed128x80' | 'ufixed128x88' | 'ufixed128x96' | 'ufixed128x104' | 'ufixed128x112' | 'ufixed128x120' | 'ufixed128x128' | 'ufixed136x8' | 'ufixed136x16' | 'ufixed136x24' | 'ufixed136x32' | 'ufixed136x40' | 'ufixed136x48' | 'ufixed136x56' | 'ufixed136x64' | 'ufixed136x72' | 'ufixed136x80' | 'ufixed136x88' | 'ufixed136x96' | 'ufixed136x104' | 'ufixed136x112' | 'ufixed136x120' | 'ufixed144x8' | 'ufixed144x16' | 'ufixed144x24' | 'ufixed144x32' | 'ufixed144x40' | 'ufixed144x48' | 'ufixed144x56' | 'ufixed144x64' | 'ufixed144x72' | 'ufixed144x80' | 'ufixed144x88' | 'ufixed144x96' | 'ufixed144x104' | 'ufixed144x112' | 'ufixed152x8' | 'ufixed152x16' | 'ufixed152x24' | 'ufixed152x32' | 'ufixed152x40' | 'ufixed152x48' | 'ufixed152x56' | 'ufixed152x64' | 'ufixed152x72' | 'ufixed152x80' | 'ufixed152x88' | 'ufixed152x96' | 'ufixed152x104' | 'ufixed160x8' | 'ufixed160x16' | 'ufixed160x24' | 'ufixed160x32' | 'ufixed160x40' | 'ufixed160x48' | 'ufixed160x56' | 'ufixed160x64' | 'ufixed160x72' | 'ufixed160x80' | 'ufixed160x88' | 'ufixed160x96' | 'ufixed168x8' | 'ufixed168x16' | 'ufixed168x24' | 'ufixed168x32' | 'ufixed168x40' | 'ufixed168x48' | 'ufixed168x56' | 'ufixed168x64' | 'ufixed168x72' | 'ufixed168x80' | 'ufixed168x88' | 'ufixed176x8' | 'ufixed176x16' | 'ufixed176x24' | 'ufixed176x32' | 'ufixed176x40' | 'ufixed176x48' | 'ufixed176x56' | 'ufixed176x64' | 'ufixed176x72' | 'ufixed176x80' | 'ufixed184x8' | 'ufixed184x16' | 'ufixed184x24' | 'ufixed184x32' | 'ufixed184x40' | 'ufixed184x48' | 'ufixed184x56' | 'ufixed184x64' | 'ufixed184x72' | 'ufixed192x8' | 'ufixed192x16' | 'ufixed192x24' | 'ufixed192x32' | 'ufixed192x40' | 'ufixed192x48' | 'ufixed192x56' | 'ufixed192x64' | 'ufixed200x8' | 'ufixed200x16' | 'ufixed200x24' | 'ufixed200x32' | 'ufixed200x40' | 'ufixed200x48' | 'ufixed200x56' | 'ufixed208x8' | 'ufixed208x16' | 'ufixed208x24' | 'ufixed208x32' | 'ufixed208x40' | 'ufixed208x48' | 'ufixed216x8' | 'ufixed216x16' | 'ufixed216x24' | 'ufixed216x32' | 'ufixed216x40' | 'ufixed224x8' | 'ufixed224x16' | 'ufixed224x24' | 'ufixed224x32' | 'ufixed232x8' | 'ufixed232x16' | 'ufixed232x24' | 'ufixed240x8' | 'ufixed240x16' | 'ufixed248x8' +Uixed = 'ufixed' | ( 'ufixed' DecimalNumber 'x' DecimalNumber ) InlineAssemblyBlock = '{' AssemblyItem* '}' From 09ef3d8e72de27c38d86977077bb20480acbccca Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 27 Jul 2017 14:39:05 +0100 Subject: [PATCH 080/163] Add proper warning for fixed point types --- docs/types.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/types.rst b/docs/types.rst index 287d7c0b00d2..abf8bb17a3dc 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -186,7 +186,9 @@ number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are mu Fixed Point Numbers ------------------- -**COMING SOON...** +.. warning:: + Fixed point numbers are not fully supported by Solidity yet. They can be declared, but + cannot be assigned to or from. .. index:: address, literal;address From 93be0dd92309c711e7a29dac5257067b837eb7f8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 22:47:26 +0100 Subject: [PATCH 081/163] FunctionType comparison/identifer to support all statemutability levels --- libsolidity/ast/Types.cpp | 14 ++++---------- test/libsolidity/ASTJSON.cpp | 2 +- test/libsolidity/SolidityTypes.cpp | 4 ++-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 302f10227a3f..e6664895e2be 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2215,10 +2215,7 @@ string FunctionType::identifier() const case Kind::Require: id += "require";break; default: solAssert(false, "Unknown function location."); break; } - if (isConstant()) - id += "_constant"; - if (isPayable()) - id += "_payable"; + id += "_" + stateMutabilityToString(m_stateMutability); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) id += "gas"; @@ -2237,8 +2234,7 @@ bool FunctionType::operator==(Type const& _other) const FunctionType const& other = dynamic_cast(_other); if ( m_kind != other.m_kind || - isConstant() != other.isConstant() || - isPayable() != other.isPayable() || + m_stateMutability != other.stateMutability() || m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size() ) @@ -2300,10 +2296,8 @@ string FunctionType::toString(bool _short) const for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); name += ")"; - if (isConstant()) - name += " constant"; - if (isPayable()) - name += " payable"; + if (m_stateMutability != StateMutability::NonPayable) + name += " " + stateMutabilityToString(m_stateMutability); if (m_kind == Kind::External) name += " external"; if (!m_returnParameterTypes.empty()) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 4fb4f20cf052..31165922ca60 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(function_type) Json::Value retval = fun["children"][1]["children"][0]; BOOST_CHECK_EQUAL(retval["name"], "VariableDeclaration"); BOOST_CHECK_EQUAL(retval["attributes"]["name"], ""); - BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () constant external returns (uint256)"); + BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () view external returns (uint256)"); funType = retval["children"][0]; BOOST_CHECK_EQUAL(funType["attributes"]["constant"], true); BOOST_CHECK_EQUAL(funType["attributes"]["payable"], false); diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 0b5ab5164331..9f385a04dcf4 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -129,10 +129,10 @@ BOOST_AUTO_TEST_CASE(type_identifiers) BOOST_CHECK_EQUAL(t.identifier(), "t_tuple$_t_type$_t_enum$_Enum_$4_$_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_t_array$_t_string_storage_$20_storage_ptr_$__$"); TypePointer sha3fun = make_shared(strings{}, strings{}, FunctionType::Kind::SHA3); - BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3$__$returns$__$"); + BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_nonpayable$__$returns$__$"); FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); - BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal$_t_function_sha3$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_nonpayable$_t_function_sha3_nonpayable$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); TypePointer m = make_shared(Type::fromElementaryTypeName("bytes32"), s.type()); MappingType m2(Type::fromElementaryTypeName("uint64"), m); From a2aaa47ee2032b522ca62249b210c06d3ca3c441 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 00:28:13 +0100 Subject: [PATCH 082/163] Use state mutability in fallback/constructor check --- libsolidity/analysis/TypeChecker.cpp | 18 ++++++++++++++---- .../SolidityNameAndTypeResolution.cpp | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ca848dd0fc7f..0c4570391e5d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -84,8 +84,13 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { if (!function->returnParameters().empty()) m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); - if (function->isDeclaredConst()) - m_errorReporter.typeError(function->location(), "Constructor cannot be defined as constant."); + if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + function->location(), + "Constructor must be payable or non-payable, but is \"" + + stateMutabilityToString(function->stateMutability()) + + "\"." + ); if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal) m_errorReporter.typeError(function->location(), "Constructor must be public or internal."); } @@ -104,8 +109,13 @@ bool TypeChecker::visit(ContractDefinition const& _contract) fallbackFunction = function; if (_contract.isLibrary()) m_errorReporter.typeError(fallbackFunction->location(), "Libraries cannot have fallback functions."); - if (fallbackFunction->isDeclaredConst()) - m_errorReporter.typeError(fallbackFunction->location(), "Fallback function cannot be declared constant."); + if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + function->location(), + "Fallback function must be payable or non-payable, but is \"" + + stateMutabilityToString(function->stateMutability()) + + "\"." + ); if (!fallbackFunction->parameters().empty()) m_errorReporter.typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); if (!fallbackFunction->returnParameters().empty()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 1c83e1a3d0fb..fc4c795e0838 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1363,7 +1363,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier) function() constant { x = 2; } } )"; - CHECK_ERROR(text, TypeError, "Fallback function cannot be declared constant."); + CHECK_ERROR(text, TypeError, "Fallback function must be payable or non-payable"); } BOOST_AUTO_TEST_CASE(fallback_function_twice) @@ -4873,7 +4873,7 @@ BOOST_AUTO_TEST_CASE(constant_constructor) function test() constant {} } )"; - CHECK_ERROR(text, TypeError, "Constructor cannot be defined as constant."); + CHECK_ERROR(text, TypeError, "Constructor must be payable or non-payable"); } BOOST_AUTO_TEST_CASE(external_constructor) From a61c88e9fea2cb61bc9db11d3eded033a7630c45 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 00:29:59 +0100 Subject: [PATCH 083/163] Use state mutability in override error messages --- libsolidity/analysis/TypeChecker.cpp | 21 +++++++++---------- .../SolidityNameAndTypeResolution.cpp | 8 +++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0c4570391e5d..0764bf671993 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -318,17 +318,16 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (function.visibility() != super.visibility()) overrideError(function, super, "Overriding function visibility differs."); - else if (function.isDeclaredConst() && !super.isDeclaredConst()) - overrideError(function, super, "Overriding function should not be declared constant."); - - else if (!function.isDeclaredConst() && super.isDeclaredConst()) - overrideError(function, super, "Overriding function should be declared constant."); - - else if (function.isPayable() && !super.isPayable()) - overrideError(function, super, "Overriding function should not be declared payable."); - - else if (!function.isPayable() && super.isPayable()) - overrideError(function, super, "Overriding function should be declared payable."); + else if (function.stateMutability() != super.stateMutability()) + overrideError( + function, + super, + "Overriding function changes state mutability from \"" + + stateMutabilityToString(super.stateMutability()) + + "\" to \"" + + stateMutabilityToString(function.stateMutability()) + + "\"." + ); else if (functionType != superType) overrideError(function, super, "Overriding function return types differ."); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fc4c795e0838..fb2686fcf6df 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -933,7 +933,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_remove_constness) contract B { function f() constant {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, "Overriding function should be declared constant."); + CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"view\" to \"nonpayable\"."); } BOOST_AUTO_TEST_CASE(illegal_override_add_constness) @@ -942,7 +942,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_add_constness) contract B { function f() {} } contract C is B { function f() constant {} } )"; - CHECK_ERROR(text, TypeError, "Overriding function should not be declared constant."); + CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"view\"."); } BOOST_AUTO_TEST_CASE(complex_inheritance) @@ -4779,7 +4779,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable) contract B { function f() payable {} } contract C is B { function f() {} } )"; - CHECK_ERROR(text, TypeError, "Overriding function should be declared payable."); + CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"payable\" to \"nonpayable\"."); } BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) @@ -4788,7 +4788,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) contract B { function f() {} } contract C is B { function f() payable {} } )"; - CHECK_ERROR(text, TypeError, "Overriding function should not be declared payable."); + CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"payable\"."); } BOOST_AUTO_TEST_CASE(function_variable_mixin) From 33ea314a24a17e9d3906c7a4fef0298ea674eb0f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 16:55:58 +0100 Subject: [PATCH 084/163] Add statemutability to AST JSON --- libsolidity/ast/ASTJsonConverter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index abee55eedb99..ee74b1e68bb6 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -325,6 +325,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair("name", _node.name()), make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), make_pair("payable", _node.isPayable()), + make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("isConstructor", _node.isConstructor()), @@ -419,6 +420,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) setJsonNode(_node, "FunctionTypeName", { make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), + make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), From 7700f4033ec9f1f14fc798ab4f030d850c6b9db3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 17:03:06 +0100 Subject: [PATCH 085/163] Remove isDeclaredConst() from functions --- libsolidity/ast/AST.h | 2 -- libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- libsolidity/ast/ASTPrinter.cpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8a577c0caec4..53a34d32e7ab 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -608,7 +608,6 @@ 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 isDeclaredConst() const { return m_stateMutability == StateMutability::View; } 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(); } @@ -913,7 +912,6 @@ class FunctionTypeName: public TypeName return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility; } StateMutability stateMutability() const { return m_stateMutability; } - bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } private: diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index ee74b1e68bb6..3f16db232ca0 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -323,7 +323,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector> attributes = { make_pair("name", _node.name()), - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), @@ -421,7 +421,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()), + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 23eb3b64ea36..bcb5e3b9dc2e 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -105,7 +105,7 @@ bool ASTPrinter::visit(FunctionDefinition const& _node) { writeLine("FunctionDefinition \"" + _node.name() + "\"" + (_node.isPublic() ? " - public" : "") + - (_node.isDeclaredConst() ? " - const" : "")); + (_node.stateMutability() == StateMutability::View ? " - const" : "")); printSourcePart(_node); return goDeeper(); } From c1ddc791ff5ce87fa2992b7548d38c2d6e894a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 21:20:46 +0200 Subject: [PATCH 086/163] Disable Circle CI builds for now --- circle.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000000..fd506ba55cc0 --- /dev/null +++ b/circle.yml @@ -0,0 +1,10 @@ +version: 2 +jobs: + build: + branches: + ignore: + - /.*/ + docker: + - image: trzeci/emscripten:sdk-tag-1.37.18-64bit + steps: + - checkout From 47b6aa1f5b92dbc9d65de6e507d18b57c62ed35f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 21:19:09 +0100 Subject: [PATCH 087/163] Update grammar to reflect state mutability --- docs/grammar.txt | 5 +++-- docs/types.rst | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index a9838ccbc56e..90dc12ac04e2 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -25,7 +25,7 @@ ModifierDefinition = 'modifier' Identifier ParameterList? Block ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? FunctionDefinition = 'function' Identifier? ParameterList - ( ModifierInvocation | 'constant' | 'payable' | 'external' | 'public' | 'internal' | 'private' )* + ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' )* ( 'returns' ParameterList )? ( ';' | Block ) EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';' @@ -50,9 +50,10 @@ UserDefinedTypeName = Identifier ( '.' Identifier )* Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | 'constant' | 'payable' )* +FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )* ( 'returns' TypeNameList )? StorageLocation = 'memory' | 'storage' +StateMutability = 'constant' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | diff --git a/docs/types.rst b/docs/types.rst index abf8bb17a3dc..13c848c26d71 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -337,7 +337,7 @@ be passed via and returned from external function calls. Function types are notated as follows:: - function () {internal|external} [constant] [payable] [returns ()] + function () {internal|external} [constant|payable] [returns ()] In contrast to the parameter types, the return types cannot be empty - if the function type should not return anything, the whole ``returns ()`` From d5f01460f99d0aa6b68e62be00f348538280ad23 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 21:22:59 +0100 Subject: [PATCH 088/163] Allow constant modifier on state variables in grammar --- docs/grammar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 90dc12ac04e2..76827a773728 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -16,7 +16,7 @@ ContractPart = StateVariableDeclaration | UsingForDeclaration InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )? -StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' )? Identifier ('=' Expression)? ';' +StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';' UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' StructDefinition = 'struct' Identifier '{' ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}' From 8025ac180f12f6f00fd29e49edd528e908ba1ca1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 21:24:20 +0100 Subject: [PATCH 089/163] Add missing keywords to documentation lexer --- docs/utils/SolidityLexer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py index ef55c6a2b8ea..a828146f5c1a 100644 --- a/docs/utils/SolidityLexer.py +++ b/docs/utils/SolidityLexer.py @@ -57,7 +57,7 @@ class SolidityLexer(RegexLexer): (r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|' r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|' r'private|return|returns|storage|super|this|throw|using|while)\b', Keyword, 'slashstartsregex'), - (r'(var|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'), + (r'(var|function|event|modifier|struct|enum|contract|library|interface)\b', Keyword.Declaration, 'slashstartsregex'), (r'(bytes|string|address|uint|int|bool|byte|' + '|'.join( ['uint%d' % (i + 8) for i in range(0, 256, 8)] + @@ -71,7 +71,7 @@ class SolidityLexer(RegexLexer): r'null|of|pure|relocatable|static|switch|try|type|typeof|view)\b', Keyword.Reserved), (r'(true|false)\b', Keyword.Constant), (r'(block|msg|tx|now|suicide|selfdestruct|addmod|mulmod|sha3|keccak256|log[0-4]|' - r'sha256|ecrecover|ripemd160|assert|revert)', Name.Builtin), + r'sha256|ecrecover|ripemd160|assert|revert|require)', Name.Builtin), (r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other), (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), (r'0x[0-9a-fA-F]+', Number.Hex), From 2a1b6b2e92edc1665eb1644f2ccf06c44e68448a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 14:26:19 +0200 Subject: [PATCH 090/163] CMake: Simplify libdevcore config --- libdevcore/CMakeLists.txt | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index c4f886a697dd..7cda130a43f5 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -1,14 +1,8 @@ -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") - -aux_source_directory(. SRC_LIST) - -set(EXECUTABLE soldevcore) - -file(GLOB HEADERS "*.h") - -include_directories(..) -add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Dev::base) - -install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +file(GLOB sources "*.cpp") +file(GLOB headers "*.h") + +add_library(devcore ${sources} ${headers}) +target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) +target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) +target_include_directories(devcore PRIVATE ..) +add_dependencies(devcore solidity_BuildInfo.h) From d1f4d110fb5c2ed09ef0111ecd3789f52be1200c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 18 Aug 2017 13:31:31 +0200 Subject: [PATCH 091/163] CMake: Build static libs by default --- cmake/EthCompilerSettings.cmake | 20 -------------------- cmake/EthDependencies.cmake | 2 -- libevmasm/CMakeLists.txt | 2 -- liblll/CMakeLists.txt | 2 -- libsolidity/CMakeLists.txt | 2 -- test/CMakeLists.txt | 2 +- 6 files changed, 1 insertion(+), 29 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 4ce9d22d9267..9b294e27ff39 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -63,13 +63,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Applying -fpermissive to a C command-line (ie. secp256k1) gives a build error. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") - # Build everything as shared libraries (.so files) - add_definitions(-DSHAREDLIB) - - # If supported for the target machine, emit position-independent code, suitable for dynamic - # linking and avoiding any limit on the size of the global offset table. - add_compile_options(-fPIC) - # Configuration-specific compiler settings. set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") @@ -198,7 +191,6 @@ elseif (DEFINED MSVC) add_compile_options(/wd4800) # disable forcing value to bool 'true' or 'false' (performance warning) (4800) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions - add_compile_options(-DMINIUPNP_STATICLIB) # define miniupnp static library # Always use Release variant of C++ runtime. # We don't want to provide Debug variants of all dependencies. Some default @@ -218,12 +210,6 @@ elseif (DEFINED MSVC) # stack size 16MB set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216") - # windows likes static - if (NOT ETH_STATIC) - message("Forcing static linkage for MSVC.") - set(ETH_STATIC 1) - endif () - # If you don't have GCC, Clang or VC++ then you are on your own. Good luck! else () message(WARNING "Your compiler is not tested, if you run into any issues, we'd welcome any patches.") @@ -262,9 +248,3 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA endif () endif () endif () - -if(ETH_STATIC) - set(BUILD_SHARED_LIBS OFF) -else() - set(BUILD_SHARED_LIBS ON) -endif(ETH_STATIC) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index a5e9b0c5f694..7d2a767529da 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -94,8 +94,6 @@ if (STATIC_LINKING) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif() - - set(ETH_STATIC ON) endif() find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS thread date_time system regex chrono filesystem unit_test_framework program_options random) diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 9cc3e93e9a80..9c7efb86325e 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -1,5 +1,3 @@ -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") - aux_source_directory(. SRC_LIST) set(EXECUTABLE solevmasm) diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index db90025a93c0..4246e481062b 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_policy(SET CMP0015 NEW) set(CMAKE_AUTOMOC OFF) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") - aux_source_directory(. SRC_LIST) set(EXECUTABLE lll) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 2342f0f9392e..e6398507a1af 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -1,5 +1,3 @@ -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") - aux_source_directory(analysis SRC_LIST) aux_source_directory(ast SRC_LIST) aux_source_directory(codegen SRC_LIST) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8e7b8916d206..6b1142394b0a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,7 +20,7 @@ eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Solidity::lll) include_directories(BEFORE ..) -target_link_libraries(${EXECUTABLE} soljson ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(${EXECUTABLE} soljson devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer soljson ${Boost_PROGRAM_OPTIONS_LIBRARIES}) From 63372e4a8567b26ad2cc419c6aa22919c5fcb28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:29:46 +0200 Subject: [PATCH 092/163] CMake: Simplify libevmasm config --- libevmasm/CMakeLists.txt | 16 +++++----------- test/CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 9c7efb86325e..914339e74ff8 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -1,12 +1,6 @@ -aux_source_directory(. SRC_LIST) +file(GLOB sources "*.cpp") +file(GLOB headers "*.h") -set(EXECUTABLE solevmasm) - -file(GLOB HEADERS "*.h") - -include_directories(BEFORE ..) -add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) -eth_use(${EXECUTABLE} REQUIRED Dev::soldevcore) -target_link_libraries(${EXECUTABLE} jsoncpp) - -install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +add_library(evmasm ${sources} ${headers}) +target_link_libraries(evmasm PUBLIC devcore jsoncpp) +target_include_directories(evmasm PUBLIC ..) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6b1142394b0a..3578f4975f7f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,4 +23,4 @@ include_directories(BEFORE ..) target_link_libraries(${EXECUTABLE} soljson devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer soljson ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) From 7337054d8e4c68bc2c82b7c01b695880a86e902b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:21:18 +0200 Subject: [PATCH 093/163] CMake: Simplify liblll config --- liblll/CMakeLists.txt | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index 4246e481062b..ceaeb6cf2e65 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -1,16 +1,6 @@ -cmake_policy(SET CMP0015 NEW) -set(CMAKE_AUTOMOC OFF) +file(GLOB sources "*.cpp") +file(GLOB headers "*.h") -aux_source_directory(. SRC_LIST) - -set(EXECUTABLE lll) - -file(GLOB HEADERS "*.h") - -include_directories(BEFORE ..) -add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Solidity::solevmasm) -#target_link_libraries(${EXECUTABLE} evmasm) - -install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +add_library(lll ${sources} ${headers}) +target_link_libraries(lll PUBLIC evmasm) +target_include_directories(lll PUBLIC ..) From 8404e2b6d76dd3681bf30e99e2cb21112b491d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:29:59 +0200 Subject: [PATCH 094/163] CMake: Simplify libsolidity config --- libsolidity/CMakeLists.txt | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index e6398507a1af..5e9c79f0cfea 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -1,21 +1,7 @@ -aux_source_directory(analysis SRC_LIST) -aux_source_directory(ast SRC_LIST) -aux_source_directory(codegen SRC_LIST) -aux_source_directory(formal SRC_LIST) -aux_source_directory(interface SRC_LIST) -aux_source_directory(parsing SRC_LIST) -aux_source_directory(inlineasm SRC_LIST) # Until we have a clear separation, libjulia has to be included here -aux_source_directory(../libjulia SRC_LIST) - -set(EXECUTABLE solidity) - -file(GLOB HEADERS "*/*.h" "../libjulia/backends/evm/*") - -include_directories(BEFORE ..) -add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Dev::soldevcore Solidity::solevmasm) - -install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") +file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") +add_library(solidity ${sources} ${headers}) +target_link_libraries(solidity PUBLIC evmasm PRIVATE ${Boost_REGEX_LIBRARIES}) +target_include_directories(solidity PUBLIC ..) From a874bd3f048125033250b8d7182dc30bbf36dbac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:21:12 +0200 Subject: [PATCH 095/163] CMake: Simplify lllc config --- lllc/CMakeLists.txt | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lllc/CMakeLists.txt b/lllc/CMakeLists.txt index 3bd11187e893..7bebd0b192e6 100644 --- a/lllc/CMakeLists.txt +++ b/lllc/CMakeLists.txt @@ -1,12 +1,3 @@ -aux_source_directory(. SRC_LIST) - -set(EXECUTABLE lllc) - -file(GLOB HEADERS "*.h") -include_directories(BEFORE ..) -eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Solidity::lll Dev::buildinfo Solidity::solevmasm) - -install( TARGETS ${EXECUTABLE} DESTINATION bin ) +add_executable(lllc main.cpp) +target_link_libraries(lllc PRIVATE lll) From 6f72025ee74d6255d40905ea09fa8a8dddf49605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:30:09 +0200 Subject: [PATCH 096/163] CMake: Simplify solc config --- solc/CMakeLists.txt | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 18e83e756492..0563fe2aa401 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -1,27 +1,19 @@ -aux_source_directory(. SRC_LIST) -list(REMOVE_ITEM SRC_LIST "./jsonCompiler.cpp") +set( + sources + CommandLineInterface.cpp CommandLineInterface.h + main.cpp +) -include_directories(BEFORE ..) +add_executable(solc ${sources}) +target_link_libraries(solc PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES}) -set(EXECUTABLE solc) - -file(GLOB HEADERS "*.h") -eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Solidity::solidity) -target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARIES}) - -if (APPLE) - install(TARGETS ${EXECUTABLE} DESTINATION bin) -else() - eth_install_executable(${EXECUTABLE}) -endif() +include(GNUInstallDirs) +install(TARGETS solc DESTINATION "${CMAKE_INSTALL_BINDIR}") if (EMSCRIPTEN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") - add_executable(soljson jsonCompiler.cpp ${HEADERS}) - eth_use(soljson REQUIRED Solidity::solidity) + add_executable(soljson jsonCompiler.cpp) else() - add_library(soljson jsonCompiler.cpp ${HEADERS}) - target_link_libraries(soljson solidity) + add_library(soljson jsonCompiler.cpp) endif() +target_link_libraries(soljson PRIVATE solidity) \ No newline at end of file From 397a72107abbe691d9e04dcb6bcc0eb2b2a583fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:38:18 +0200 Subject: [PATCH 097/163] CMake: Simplify test tools config --- test/CMakeLists.txt | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3578f4975f7f..92218f1d99f8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,26 +1,9 @@ -cmake_policy(SET CMP0015 NEW) +file(GLOB_RECURSE sources "*.cpp") +list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") +file(GLOB_RECURSE headers "*.h") -aux_source_directory(. SRC_LIST) -aux_source_directory(libdevcore SRC_LIST) -aux_source_directory(libevmasm SRC_LIST) -aux_source_directory(libsolidity SRC_LIST) -aux_source_directory(libjulia SRC_LIST) -aux_source_directory(contracts SRC_LIST) -aux_source_directory(liblll SRC_LIST) -aux_source_directory(libjulia SRC_LIST) - -list(REMOVE_ITEM SRC_LIST "./fuzzer.cpp") - -get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) - -file(GLOB HEADERS "*.h" "*/*.h") -set(EXECUTABLE soltest) -eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - -eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Solidity::lll) - -include_directories(BEFORE ..) -target_link_libraries(${EXECUTABLE} soljson devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +add_executable(soltest ${sources} ${headers}) +target_link_libraries(soltest PRIVATE soljson solidity lll ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) From 0712e6302e728bba281f41afa74e6c74638a75c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 17 Aug 2017 11:56:49 +0200 Subject: [PATCH 098/163] Boost: Use static libs --- cmake/EthDependencies.cmake | 55 ++----------------------------------- libdevcore/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 54 deletions(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 7d2a767529da..140373071d1f 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -43,60 +43,9 @@ find_program(CTEST_COMMAND ctest) ## use multithreaded boost libraries, with -mt suffix set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_LIBS ON) -if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - -# use static boost libraries *.lib - set(Boost_USE_STATIC_LIBS ON) - -elseif (APPLE) - -# use static boost libraries *.a - set(Boost_USE_STATIC_LIBS ON) - -elseif (UNIX) -# use dynamic boost libraries *.dll - set(Boost_USE_STATIC_LIBS OFF) - -endif() - -set(STATIC_LINKING FALSE CACHE BOOL "Build static binaries") - -if (STATIC_LINKING) - - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_STATIC_RUNTIME ON) - - set(OpenSSL_USE_STATIC_LIBS ON) - - if (MSVC) - # TODO - Why would we need .a on Windows? Maybe some Cygwin-ism. - # When I work through Windows static linkage, I will remove this, - # if that is possible. - set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) - elseif (APPLE) - # At the time of writing, we are still only PARTIALLY statically linked - # on OS X, with a mixture of statically linked external libraries where - # those are available, and dynamically linked where that is the only - # option we have. Ultimately, the aim would be for everything except - # the runtime libraries to be statically linked. - # - # Still TODO: - # - jsoncpp - # - json-rpc-cpp - # - leveldb (which pulls in snappy, for the dylib at ;east) - # - miniupnp - # - gmp - # - # Two further libraries (curl and zlib) ship as dylibs with the platform - # but again we could build from source and statically link these too. - set(CMAKE_FIND_LIBRARY_SUFFIXES .a .dylib) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - endif() -endif() - -find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS thread date_time system regex chrono filesystem unit_test_framework program_options random) +find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options) eth_show_dependency(Boost boost) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 7cda130a43f5..4b15427f9cc8 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) +target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories(devcore PRIVATE ..) add_dependencies(devcore solidity_BuildInfo.h) From fe25bcf350bfb65ddb69bc5e7d3f65dfa6d23fa7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 22:19:08 +0100 Subject: [PATCH 099/163] Library cannot have constructors --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 3 +++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7d026862e832..f10460320b7e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Features: Bugfixes: * Parser: Enforce commas between array and tuple elements. * Parser: Limit maximum recursion depth. + * Type Checker: Disallow constructors in libraries. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0764bf671993..e5660cde0da8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -546,6 +546,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isConstructor()) m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } + else if (m_scope->contractKind() == ContractDefinition::ContractKind::Library) + if (_function.isConstructor()) + m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in libraries."); if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fb2686fcf6df..fad1ca61ca13 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3055,6 +3055,16 @@ BOOST_AUTO_TEST_CASE(library_having_variables) CHECK_ERROR(text, TypeError, "Library cannot have non-constant state variables"); } +BOOST_AUTO_TEST_CASE(library_constructor) +{ + char const* text = R"( + library Lib { + function Lib(); + } + )"; + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Constructor cannot be defined in libraries."); +} + BOOST_AUTO_TEST_CASE(valid_library) { char const* text = R"( From 99d198ffb034839e82649f10814210c0374d40d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 17 Aug 2017 15:48:39 +0200 Subject: [PATCH 100/163] CMake: Make libs dependencies explicit --- liblll/CMakeLists.txt | 2 +- libsolidity/CMakeLists.txt | 2 +- test/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index ceaeb6cf2e65..1cc37da32c39 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -2,5 +2,5 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(lll ${sources} ${headers}) -target_link_libraries(lll PUBLIC evmasm) +target_link_libraries(lll PUBLIC evmasm devcore) target_include_directories(lll PUBLIC ..) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 5e9c79f0cfea..ebd09c5fda7b 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") add_library(solidity ${sources} ${headers}) -target_link_libraries(solidity PUBLIC evmasm PRIVATE ${Boost_REGEX_LIBRARIES}) +target_link_libraries(solidity PUBLIC evmasm devcore PRIVATE ${Boost_REGEX_LIBRARIES}) target_include_directories(solidity PUBLIC ..) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 92218f1d99f8..6a8a43991242 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,7 @@ list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") file(GLOB_RECURSE headers "*.h") add_executable(soltest ${sources} ${headers}) -target_link_libraries(soltest PRIVATE soljson solidity lll ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(soltest PRIVATE soljson solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) From fa534946262ca4a13c94a9ef012c0af014827871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 21 Aug 2017 12:37:55 +0200 Subject: [PATCH 101/163] CMake: Remove libsolidity <- boost::regex dependency --- libsolidity/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index ebd09c5fda7b..fd27a492128c 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") add_library(solidity ${sources} ${headers}) -target_link_libraries(solidity PUBLIC evmasm devcore PRIVATE ${Boost_REGEX_LIBRARIES}) +target_link_libraries(solidity PUBLIC evmasm devcore) target_include_directories(solidity PUBLIC ..) From 2c5985de06b4ad35eaa77f3869e280440100c9cd Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 12:08:29 +0200 Subject: [PATCH 102/163] Be more strict about number literals in assembly. --- Changelog.md | 1 + libsolidity/inlineasm/AsmParser.cpp | 21 +++++++++++++++++++++ libsolidity/inlineasm/AsmParser.h | 2 ++ test/libjulia/Parser.cpp | 8 ++++++++ test/libsolidity/InlineAssembly.cpp | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/Changelog.md b/Changelog.md index 7d026862e832..a587d3adf3f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Features: Bugfixes: * Parser: Enforce commas between array and tuple elements. * Parser: Limit maximum recursion depth. + * Assembly Parser: Be more strict about number literals. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 133f70b10c27..1dcc42b808e0 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -23,6 +23,9 @@ #include #include #include + +#include + #include #include @@ -297,6 +300,8 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) kind = LiteralKind::String; break; case Token::Number: + if (!isValidNumberLiteral(currentLiteral())) + fatalParserError("Invalid number literal."); kind = LiteralKind::Number; break; case Token::TrueLiteral: @@ -501,3 +506,19 @@ string Parser::expectAsmIdentifier() expectToken(Token::Identifier); return name; } + +bool Parser::isValidNumberLiteral(string const& _literal) +{ + try + { + u256(_literal); + } + catch (...) + { + return false; + } + if (boost::starts_with(_literal, "0x")) + return true; + else + return _literal.find_first_not_of("0123456789") == string::npos; +} diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 45708afd354f..a48a33936a13 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -75,6 +75,8 @@ class Parser: public ParserBase TypedName parseTypedName(); std::string expectAsmIdentifier(); + static bool isValidNumberLiteral(std::string const& _literal); + private: bool m_julia = false; }; diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index dd6f3d94b48e..e1bf5a3a3abd 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -214,6 +214,14 @@ BOOST_AUTO_TEST_CASE(invalid_types) CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); } +BOOST_AUTO_TEST_CASE(number_literals) +{ + BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); + 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."); +} + BOOST_AUTO_TEST_CASE(builtin_types) { BOOST_CHECK(successParse("{ let x:bool := true:bool }")); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 4bf4eb48575b..8e1c304acd6b 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -339,6 +339,14 @@ BOOST_AUTO_TEST_CASE(blocks) BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); } +BOOST_AUTO_TEST_CASE(number_literals) +{ + BOOST_CHECK(successParse("{ let x := 1 }")); + 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."); +} + BOOST_AUTO_TEST_CASE(function_definitions) { BOOST_CHECK(successParse("{ function f() { } function g(a) -> x { } }")); From b3986f1d079a98b0894c735111880eb23b177f19 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 14:08:58 +0100 Subject: [PATCH 103/163] Remove some unused includes --- libdevcore/Common.h | 1 - libsolidity/interface/ABI.cpp | 1 - libsolidity/interface/CompilerStack.cpp | 2 -- test/Metadata.cpp | 1 - test/libevmasm/Optimiser.cpp | 1 - test/libsolidity/JSONCompiler.cpp | 2 -- test/libsolidity/StandardCompiler.cpp | 2 -- 7 files changed, 10 deletions(-) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index c5b09a80b230..d383e74d980a 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -44,7 +44,6 @@ #include #include #include -#include #if defined(__GNUC__) #pragma warning(push) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 9fc2f4e82a77..a7224e86a0f9 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -19,7 +19,6 @@ */ #include -#include #include using namespace std; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 70bebfa53e2c..19bfbe63eae2 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -49,8 +49,6 @@ #include #include -#include - using namespace std; using namespace dev; diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 03f905b1bd98..e4de0a6b802c 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -21,7 +21,6 @@ #include #include -#include #include using namespace std; diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 5aa81af50875..6656f15bea98 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 536ba730e0eb..0fe7636c94ae 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -20,8 +20,6 @@ */ #include -#include -#include #include #include #include diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index be13d46b8367..79848c36dda2 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -20,8 +20,6 @@ */ #include -#include -#include #include #include #include From ec82706396aa1770851223d59c0efdde7407fda0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 16:22:20 +0200 Subject: [PATCH 104/163] Fix crash related to ``using for`` without a library. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 2 +- .../libsolidity/SolidityNameAndTypeResolution.cpp | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 49d92eb5e98d..3574455b73c7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ Bugfixes: * Assembly Parser: Be more strict about number literals. * Parser: Enforce commas between array and tuple elements. * Parser: Limit maximum recursion depth. + * Type Checker: Crash fix related to ``using``. * Type Checker: Disallow constructors in libraries. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e5660cde0da8..32d078fd6928 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -463,7 +463,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) _usingFor.libraryName().annotation().referencedDeclaration ); if (!library || !library->isLibrary()) - m_errorReporter.typeError(_usingFor.libraryName().location(), "Library name expected."); + m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected."); } bool TypeChecker::visit(StructDefinition const& _struct) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fad1ca61ca13..e349bf83ae94 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6657,6 +6657,21 @@ BOOST_AUTO_TEST_CASE(library_function_without_implementation) CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared."); } +BOOST_AUTO_TEST_CASE(using_for_with_non_library) +{ + // This tests a crash that was resolved by making the first error fatal. + char const* text = R"( + library L { + struct S { uint d; } + using S for S; + function f(S _s) internal { + _s.d = 1; + } + } + )"; + CHECK_ERROR(text, TypeError, "Library name expected."); +} + BOOST_AUTO_TEST_CASE(experimental_pragma) { char const* text = R"( From e3f90565d8f623537072d84316d476343c2b06ad Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 27 Jul 2017 16:40:01 +0200 Subject: [PATCH 105/163] Avoid some Json copy operations. --- libevmasm/Assembly.cpp | 7 ++----- libsolidity/ast/ASTJsonConverter.cpp | 31 +++++++++++----------------- libsolidity/ast/ASTJsonConverter.h | 13 ++++++++++-- solc/CommandLineInterface.cpp | 3 +-- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 42b923df48c1..0a3bf6b8b546 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -233,7 +233,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes { Json::Value root; - Json::Value collection(Json::arrayValue); + Json::Value& collection = root[".code"] = Json::arrayValue; for (AssemblyItem const& i: m_items) { switch (i.type()) @@ -289,11 +289,9 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes } } - root[".code"] = collection; - if (!m_data.empty() || !m_subs.empty()) { - Json::Value data; + Json::Value& data = root[".data"] = Json::objectValue; for (auto const& i: m_data) if (u256(i.first) >= m_subs.size()) data[toStringInHex((u256)i.first)] = toHex(i.second); @@ -304,7 +302,6 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes hexStr << hex << i; data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true); } - root[".data"] = data; } if (m_auxiliaryData.size() > 0) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 3f16db232ca0..fdec8945443a 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -15,8 +15,7 @@ along with solidity. If not, see . */ /** - * @author Lefteris - * @date 2015 + * @date 2017 * Converts the AST into json format */ @@ -81,28 +80,22 @@ void ASTJsonConverter::setJsonNode( (_nodeType == "InlineAssembly") || (_nodeType == "Throw") ) - { - Json::Value children(Json::arrayValue); - m_currentValue["children"] = children; - } + m_currentValue["children"] = Json::arrayValue; for (auto& e: _attributes) { - if ( - (!e.second.isNull()) && - ( - (e.second.isObject() && e.second.isMember("name")) || - (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || - (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] - ) - ) + if ((!e.second.isNull()) && ( + (e.second.isObject() && e.second.isMember("name")) || + (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || + (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] + )) { if (e.second.isObject()) - m_currentValue["children"].append(std::move(e.second)); + appendMove(m_currentValue["children"], std::move(e.second)); if (e.second.isArray()) for (auto& child: e.second) if (!child.isNull()) - m_currentValue["children"].append(std::move(child)); + appendMove(m_currentValue["children"], std::move(child)); } else { @@ -147,7 +140,7 @@ Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr Json::Value toJson(std::vector> const& _nodes) { Json::Value ret(Json::arrayValue); for (auto const& n: _nodes) - ret.append(n ? toJson(*n) : Json::nullValue); + if (n) + appendMove(ret, toJson(*n)); + else + ret.append(Json::nullValue); return ret; } bool visit(SourceUnit const& _node) override; @@ -155,6 +158,12 @@ class ASTJsonConverter: public ASTConstVisitor std::vector> &_attributes, ExpressionAnnotation const& _annotation ); + static void appendMove(Json::Value& _array, Json::Value&& _value) + { + solAssert(_array.isArray(), ""); + _array.append(std::move(_value)); + } + bool m_legacy = false; ///< if true, use legacy format bool m_inEvent = false; ///< whether we are currently inside an event or not Json::Value m_currentValue; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 152526f4efbe..bfc53aef5520 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -851,7 +851,7 @@ void CommandLineInterface::handleCombinedJSON() output[g_strContracts] = Json::Value(Json::objectValue); for (string const& contractName: contracts) { - Json::Value contractData(Json::objectValue); + Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; if (requests.count(g_strAbi)) contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName)); if (requests.count("metadata")) @@ -885,7 +885,6 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strNatspecDev] = dev::jsonCompactPrint(m_compiler->natspecDev(contractName)); if (requests.count(g_strNatspecUser)) contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspecUser(contractName)); - output[g_strContracts][contractName] = contractData; } bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); From 4b56829ac3dc28ea9ca6945fee22963149bcc2bd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 17 Aug 2017 02:13:16 +0100 Subject: [PATCH 106/163] Create children node in ASTJsonConverter when neccesary --- libsolidity/ast/ASTJsonConverter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index fdec8945443a..6d54180371b0 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -91,11 +91,19 @@ void ASTJsonConverter::setJsonNode( )) { if (e.second.isObject()) + { + if (!m_currentValue["children"].isArray()) + m_currentValue["children"] = Json::arrayValue; appendMove(m_currentValue["children"], std::move(e.second)); + } if (e.second.isArray()) for (auto& child: e.second) if (!child.isNull()) + { + if (!m_currentValue["children"].isArray()) + m_currentValue["children"] = Json::arrayValue; appendMove(m_currentValue["children"], std::move(child)); + } } else { From 0dc73913e1b72e89ffdbf05592798d4bbfbf1714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 21 Aug 2017 20:42:06 +0200 Subject: [PATCH 107/163] CMake: Allow linking Boost dynamically Make Boost_USE_STATIC_LIBS an CMake option, ON by default. --- cmake/EthDependencies.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 140373071d1f..39ce060e8c8a 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -43,7 +43,7 @@ find_program(CTEST_COMMAND ctest) ## use multithreaded boost libraries, with -mt suffix set(Boost_USE_MULTITHREADED ON) -set(Boost_USE_STATIC_LIBS ON) +option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options) From c94b1f81730c27480cb5813a7a909511613c14c5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 17 Aug 2017 00:07:07 +0100 Subject: [PATCH 108/163] Set variable to nullptr in ASTPrinter --- libsolidity/ast/ASTPrinter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 4a37f17f8454..193f929663d8 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -146,7 +146,7 @@ class ASTPrinter: public ASTConstVisitor std::string m_source; ASTNode const* m_ast; GasEstimator::ASTGasConsumption m_gasCosts; - std::ostream* m_ostream; + std::ostream* m_ostream = nullptr; }; } From b25f0c52ac01857a82dda20ec2b646c7ae90cd7d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 12 Aug 2017 00:45:37 +0100 Subject: [PATCH 109/163] Reject the creation of interface with the new statement --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 6 +++++ libsolidity/ast/Types.cpp | 3 +++ .../SolidityNameAndTypeResolution.cpp | 26 +++++++++++++++++++ 4 files changed, 36 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3574455b73c7..39cbd44bec17 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,7 @@ Bugfixes: * Parser: Limit maximum recursion depth. * Type Checker: Crash fix related to ``using``. * Type Checker: Disallow constructors in libraries. + * Type Checker: Reject the creation of interface contracts using the ``new`` statement. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 32d078fd6928..f70b75d3d992 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -429,6 +429,10 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) if (base->isLibrary()) m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); + // Interface can have no constructors - no need to validate + if (base->contractKind() == ContractDefinition::ContractKind::Interface) + return; + auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); if (!arguments.empty() && parameterTypes.size() != arguments.size()) @@ -1554,6 +1558,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + if (contract->contractKind() == ContractDefinition::ContractKind::Interface) + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) m_errorReporter.typeError( _newExpression.location(), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e6664895e2be..adf41229a536 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2140,6 +2140,8 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c strings parameterNames; StateMutability stateMutability = StateMutability::NonPayable; + solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, ""); + if (constructor) { for (ASTPointer const& var: constructor->parameters()) @@ -2150,6 +2152,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c if (constructor->isPayable()) stateMutability = StateMutability::Payable; } + return make_shared( parameters, TypePointers{make_shared(_contract)}, diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e349bf83ae94..a4fc9c9864a0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6709,6 +6709,32 @@ BOOST_AUTO_TEST_CASE(experimental_pragma) // CHECK_ERROR_ALLOW_MULTI(text, SyntaxError, "Duplicate experimental feature name."); } +BOOST_AUTO_TEST_CASE(reject_interface_creation) +{ + char const* text = R"( + interface I {} + contract C { + function f() { + new I(); + } + } + )"; + CHECK_ERROR(text, TypeError, "Cannot instantiate an interface."); +} + +BOOST_AUTO_TEST_CASE(accept_library_creation) +{ + char const* text = R"( + library L {} + contract C { + function f() { + new L(); + } + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_SUITE_END() } From e2cfc9ee92158169b5dd058074f25d67959e9875 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 17 Aug 2017 01:14:15 +0100 Subject: [PATCH 110/163] Mark a lot of functions const (where possible) --- libevmasm/ConstantOptimiser.cpp | 10 +++++----- libevmasm/ConstantOptimiser.h | 22 +++++++++++----------- liblll/CompilerState.cpp | 2 +- liblll/CompilerState.h | 2 +- libsolidity/ast/ASTJsonConverter.h | 6 +++--- libsolidity/codegen/ABIFunctions.cpp | 2 +- libsolidity/codegen/ABIFunctions.h | 2 +- libsolidity/codegen/Compiler.h | 6 +++--- libsolidity/codegen/CompilerContext.h | 4 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.h | 2 +- libsolidity/inlineasm/AsmParser.h | 2 +- libsolidity/inlineasm/AsmPrinter.cpp | 2 +- libsolidity/inlineasm/AsmPrinter.h | 2 +- libsolidity/inlineasm/AsmScope.cpp | 2 +- libsolidity/inlineasm/AsmScope.h | 2 +- libsolidity/interface/CompilerStack.h | 2 +- libsolidity/parsing/Scanner.h | 10 +++++----- 18 files changed, 41 insertions(+), 41 deletions(-) diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 2ecbfa7f5a56..a7a87c35b677 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -124,7 +124,7 @@ void ConstantOptimisationMethod::replaceConstants( _items = std::move(replaced); } -bigint LiteralMethod::gasNeeded() +bigint LiteralMethod::gasNeeded() const { return combineGas( simpleRunGas({Instruction::PUSH1}), @@ -139,7 +139,7 @@ CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value): { } -bigint CodeCopyMethod::gasNeeded() +bigint CodeCopyMethod::gasNeeded() const { return combineGas( // Run gas: we ignore memory increase costs @@ -151,7 +151,7 @@ bigint CodeCopyMethod::gasNeeded() ); } -AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) +AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const { bytes data = toBigEndian(m_value); AssemblyItems actualCopyRoutine = copyRoutine(); @@ -234,7 +234,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) } } -bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) +bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const { // This is a tiny EVM that can only evaluate some instructions. vector stack; @@ -282,7 +282,7 @@ bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& return stack.size() == 1 && stack.front() == _value; } -bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) +bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP); return combineGas( diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 85bdabac66fa..19f14f7a076a 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -63,11 +63,11 @@ class ConstantOptimisationMethod explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): m_params(_params), m_value(_value) {} - virtual bigint gasNeeded() = 0; + virtual bigint gasNeeded() const = 0; /// Executes the method, potentially appending to the assembly and returns a vector of /// assembly items the constant should be relpaced with in one sweep. /// If the vector is empty, the constants will not be deleted. - virtual AssemblyItems execute(Assembly& _assembly) = 0; + virtual AssemblyItems execute(Assembly& _assembly) const = 0; protected: size_t dataSize() const { return std::max(1, dev::bytesRequired(m_value)); } @@ -84,7 +84,7 @@ class ConstantOptimisationMethod bigint const& _runGas, bigint const& _repeatedDataGas, bigint const& _uniqueDataGas - ) + ) const { // _runGas is not multiplied by _multiplicity because the runs are "per opcode" return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas; @@ -106,8 +106,8 @@ class LiteralMethod: public ConstantOptimisationMethod public: explicit LiteralMethod(Params const& _params, u256 const& _value): ConstantOptimisationMethod(_params, _value) {} - virtual bigint gasNeeded() override; - virtual AssemblyItems execute(Assembly&) override { return AssemblyItems{}; } + virtual bigint gasNeeded() const override; + virtual AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; } }; /** @@ -117,8 +117,8 @@ class CodeCopyMethod: public ConstantOptimisationMethod { public: explicit CodeCopyMethod(Params const& _params, u256 const& _value); - virtual bigint gasNeeded() override; - virtual AssemblyItems execute(Assembly& _assembly) override; + virtual bigint gasNeeded() const override; + virtual AssemblyItems execute(Assembly& _assembly) const override; protected: AssemblyItems const& copyRoutine() const; @@ -141,8 +141,8 @@ class ComputeMethod: public ConstantOptimisationMethod ); } - virtual bigint gasNeeded() override { return gasNeeded(m_routine); } - virtual AssemblyItems execute(Assembly&) override + virtual bigint gasNeeded() const override { return gasNeeded(m_routine); } + virtual AssemblyItems execute(Assembly&) const override { return m_routine; } @@ -151,8 +151,8 @@ class ComputeMethod: public ConstantOptimisationMethod /// Tries to recursively find a way to compute @a _value. AssemblyItems findRepresentation(u256 const& _value); /// Recomputes the value from the calculated representation and checks for correctness. - bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine); - bigint gasNeeded(AssemblyItems const& _routine); + bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const; + bigint gasNeeded(AssemblyItems const& _routine) const; /// Counter for the complexity of optimization, will stop when it reaches zero. size_t m_maxSteps = 10000; diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index 9701e16b7d99..d53dec7e5c2d 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -30,7 +30,7 @@ CompilerState::CompilerState() { } -CodeFragment const& CompilerState::getDef(std::string const& _s) +CodeFragment const& CompilerState::getDef(std::string const& _s) const { if (defs.count(_s)) return defs.at(_s); diff --git a/liblll/CompilerState.h b/liblll/CompilerState.h index c29d3b7d371f..96a0246de9bf 100644 --- a/liblll/CompilerState.h +++ b/liblll/CompilerState.h @@ -40,7 +40,7 @@ struct CompilerState { CompilerState(); - CodeFragment const& getDef(std::string const& _s); + CodeFragment const& getDef(std::string const& _s) const; void populateStandard(); unsigned stackSize = 128; diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 70e260db8ddf..cc589d4a42fc 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -119,7 +119,7 @@ class ASTJsonConverter: public ASTConstVisitor ); std::string sourceLocationToString(SourceLocation const& _location) const; std::string namePathToString(std::vector const& _namePath) const; - Json::Value idOrNull(ASTNode const* _pt) + Json::Value idOrNull(ASTNode const* _pt) const { return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue; } @@ -134,12 +134,12 @@ class ASTJsonConverter: public ASTConstVisitor std::string literalTokenKind(Token::Value _token); std::string type(Expression const& _expression); std::string type(VariableDeclaration const& _varDecl); - int nodeId(ASTNode const& _node) + int nodeId(ASTNode const& _node) const { return _node.id(); } template - Json::Value getContainerIds(Container const& container) + Json::Value getContainerIds(Container const& container) const { Json::Value tmp(Json::arrayValue); for (auto const& element: container) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a2938ed796e9..2313473f55cf 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -1056,7 +1056,7 @@ string ABIFunctions::createFunction(string const& _name, function con return _name; } -size_t ABIFunctions::headSize(TypePointers const& _targetTypes) +size_t ABIFunctions::headSize(TypePointers const& _targetTypes) const { size_t headSize = 0; for (auto const& t: _targetTypes) diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 76f4b46774c1..103df5aee441 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -162,7 +162,7 @@ class ABIFunctions std::string createFunction(std::string const& _name, std::function const& _creator); /// @returns the size of the static part of the encoding of the given types. - size_t headSize(TypePointers const& _targetTypes); + size_t headSize(TypePointers const& _targetTypes) const; /// Map from function name to code for a multi-use function. std::map m_requestedFunctions; diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index eef078c11d8a..8c63ea9c54d9 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -51,9 +51,9 @@ class Compiler ContractDefinition const& _contract, std::map const& _contracts ); - eth::Assembly const& assembly() { return m_context.assembly(); } - eth::LinkerObject assembledObject() { return m_context.assembledObject(); } - eth::LinkerObject runtimeObject() { return m_context.assembledRuntimeObject(m_runtimeSub); } + eth::Assembly const& assembly() const { return m_context.assembly(); } + eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } + eth::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } /// @arg _sourceCodes is the map of input files to source code strings /// @arg _inJsonFromat shows whether the out should be in Json format Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 583360eae422..96cbf6c1115d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -208,8 +208,8 @@ class CompilerContext return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat); } - eth::LinkerObject const& assembledObject() { return m_asm->assemble(); } - eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm->sub(_subIndex).assemble(); } + eth::LinkerObject const& assembledObject() const { return m_asm->assemble(); } + eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } /** * Helper class to pop the visited nodes stack when a scope closes diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 55d35c44c728..639bfc324f97 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1811,7 +1811,7 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) const { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 3b8cf1c627ba..5f6c3d64dd8d 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -119,7 +119,7 @@ class ExpressionCompiler: private ASTConstVisitor /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - bool cleanupNeededForOp(Type::Category _type, Token::Value _op); + bool cleanupNeededForOp(Type::Category _type, Token::Value _op) const; /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index a48a33936a13..e46d1732e5a6 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -45,7 +45,7 @@ class Parser: public ParserBase protected: /// Creates an inline assembly node with the given source location. - template T createWithLocation(SourceLocation const& _loc = SourceLocation()) + template T createWithLocation(SourceLocation const& _loc = SourceLocation()) const { T r; r.location = _loc; diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 4f96a3e968ed..47ede91d4174 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -209,7 +209,7 @@ string AsmPrinter::operator()(Block const& _block) return "{\n " + body + "\n}"; } -string AsmPrinter::appendTypeName(std::string const& _type) +string AsmPrinter::appendTypeName(std::string const& _type) const { if (m_julia) return ":" + _type; diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index f57dddc856e2..665206326a9e 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -53,7 +53,7 @@ class AsmPrinter: public boost::static_visitor std::string operator()(assembly::Block const& _block); private: - std::string appendTypeName(std::string const& _type); + std::string appendTypeName(std::string const& _type) const; bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index 315d5953fbfe..64d5bd9ad0bc 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -70,7 +70,7 @@ Scope::Identifier* Scope::lookup(string const& _name) return nullptr; } -bool Scope::exists(string const& _name) +bool Scope::exists(string const& _name) const { if (identifiers.count(_name)) return true; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index cc24056525ee..447d6490d6e3 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -107,7 +107,7 @@ struct Scope } /// @returns true if the name exists in this scope or in super scopes (also searches /// across function and assembly boundaries). - bool exists(std::string const& _name); + bool exists(std::string const& _name) const; /// @returns the number of variables directly registered inside the scope. size_t numberOfVariables() const; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 54e3e23f89a6..bb0f4126cc4f 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -88,7 +88,7 @@ class CompilerStack: boost::noncopyable m_errorReporter(m_errorList) {} /// @returns the list of errors that occured during parsing and type checking. - ErrorList const& errors() { return m_errorReporter.errors(); } + ErrorList const& errors() const { return m_errorReporter.errors(); } /// @returns the current state. State state() const { return m_stackState; } diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index d6b48c6f8de4..0adaa6fd2e2f 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -75,7 +75,7 @@ class CharStream int position() const { return m_position; } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; } - char advanceAndGet(size_t _chars=1); + char advanceAndGet(size_t _chars = 1); char rollback(size_t _amount); void reset() { m_position = 0; } @@ -118,11 +118,11 @@ class Scanner ///@name Information about the current token /// @returns the current token - Token::Value currentToken() + Token::Value currentToken() const { return m_currentToken.token; } - ElementaryTypeNameToken currentElementaryTypeNameToken() + ElementaryTypeNameToken currentElementaryTypeNameToken() const { unsigned firstSize; unsigned secondSize; @@ -219,8 +219,8 @@ class Scanner bool scanEscape(); /// Return the current source position. - int sourcePos() { return m_source.position(); } - bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } + int sourcePos() const { return m_source.position(); } + bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } TokenDesc m_skippedComment; // desc for current skipped comment TokenDesc m_nextSkippedComment; // desc for next skiped comment From 3cf2426e1a2975a60176cb46040fccfbc182d771 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 14:22:26 +0100 Subject: [PATCH 111/163] Make vector+vector template more readable --- libdevcore/CommonData.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 6f40d7be03ca..0321011e3d0a 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -184,7 +184,8 @@ template inline std::vector operator+(std::vector const& _a, std::vector const& _b) { std::vector ret(_a); - return ret += _b; + ret += _b; + return ret; } template From 2a5772cff7d03de0d02c5358a6a6755e8f802697 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 15:42:17 +0100 Subject: [PATCH 112/163] Mark appropriate constructors explicit --- libdevcore/Common.h | 2 +- libdevcore/FixedHash.h | 2 +- libevmasm/BlockDeduplicator.h | 2 +- libevmasm/CommonSubexpressionEliminator.h | 2 +- libevmasm/PathGasMeter.h | 2 +- libsolidity/codegen/ArrayUtils.h | 2 +- libsolidity/codegen/CompilerUtils.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 2 +- libsolidity/inlineasm/AsmCodeGen.cpp | 2 +- libsolidity/interface/ErrorReporter.h | 2 +- libsolidity/interface/StandardCompiler.h | 2 +- libsolidity/parsing/Parser.cpp | 4 ++-- libsolidity/parsing/Parser.h | 2 +- libsolidity/parsing/ParserBase.h | 2 +- test/RPCSession.h | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libdevcore/Common.h b/libdevcore/Common.h index d383e74d980a..9d6dd408b118 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -157,7 +157,7 @@ template <> inline u256 exp10<0>() class ScopeGuard { public: - ScopeGuard(std::function _f): m_f(_f) {} + explicit ScopeGuard(std::function _f): m_f(_f) {} ~ScopeGuard() { m_f(); } private: diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 5b1c7acf5e63..1c37b675ae5c 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -59,7 +59,7 @@ class FixedHash enum ConstructFromHashType { AlignLeft, AlignRight, FailIfDifferent }; /// Construct an empty hash. - FixedHash() { m_data.fill(0); } + explicit FixedHash() { m_data.fill(0); } /// Construct from another hash, filling with zeroes or cropping as necessary. template explicit FixedHash(FixedHash const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; } diff --git a/libevmasm/BlockDeduplicator.h b/libevmasm/BlockDeduplicator.h index 797c2476336b..5640984b3e3e 100644 --- a/libevmasm/BlockDeduplicator.h +++ b/libevmasm/BlockDeduplicator.h @@ -45,7 +45,7 @@ using AssemblyItems = std::vector; class BlockDeduplicator { public: - BlockDeduplicator(AssemblyItems& _items): m_items(_items) {} + explicit BlockDeduplicator(AssemblyItems& _items): m_items(_items) {} /// @returns true if something was changed bool deduplicate(); /// @returns the tags that were replaced. diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 83fc97323ab6..f247320017dd 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -61,7 +61,7 @@ class CommonSubexpressionEliminator using Id = ExpressionClasses::Id; using StoreOperation = KnownState::StoreOperation; - CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {} + explicit CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {} /// 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. diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 0a0fe5d02ed1..4826eac2a453 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -50,7 +50,7 @@ struct GasPath class PathGasMeter { public: - PathGasMeter(AssemblyItems const& _items); + explicit PathGasMeter(AssemblyItems const& _items); GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr const& _state); diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 806fbea7b37f..f3ddc4ee84a9 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -40,7 +40,7 @@ using TypePointer = std::shared_ptr; class ArrayUtils { public: - ArrayUtils(CompilerContext& _context): m_context(_context) {} + explicit ArrayUtils(CompilerContext& _context): m_context(_context) {} /// Copies an array to an array in storage. The arrays can be of different types only if /// their storage representation is the same. diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 0942778860f5..18b70250084d 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -33,7 +33,7 @@ class Type; // forward class CompilerUtils { public: - CompilerUtils(CompilerContext& _context): m_context(_context) {} + explicit CompilerUtils(CompilerContext& _context): m_context(_context) {} /// Stores the initial value of the free-memory-pointer at its position; void initialiseFreeMemoryPointer(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 29a22faeb094..e53f1b94ed9e 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -45,7 +45,7 @@ using namespace dev::solidity; class StackHeightChecker { public: - StackHeightChecker(CompilerContext const& _context): + explicit StackHeightChecker(CompilerContext const& _context): m_context(_context), stackHeight(m_context.stackHeight()) {} void check() { solAssert(m_context.stackHeight() == stackHeight, std::string("I sense a disturbance in the stack: ") + std::to_string(m_context.stackHeight()) + " vs " + std::to_string(stackHeight)); } private: diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 7474373728f3..24aa4ba8dc77 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -52,7 +52,7 @@ using namespace dev::solidity::assembly; class EthAssemblyAdapter: public julia::AbstractAssembly { public: - EthAssemblyAdapter(eth::Assembly& _assembly): + explicit EthAssemblyAdapter(eth::Assembly& _assembly): m_assembly(_assembly) { } diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 12f4e8dff328..241d6b438bfd 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -36,7 +36,7 @@ class ErrorReporter { public: - ErrorReporter(ErrorList& _errors): + explicit ErrorReporter(ErrorList& _errors): m_errorList(_errors) { } ErrorReporter& operator=(ErrorReporter const& _errorReporter); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index dfaf88cd5824..d9787a40b437 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -40,7 +40,7 @@ class StandardCompiler: boost::noncopyable /// Creates a new StandardCompiler. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback()) + explicit StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback()) : m_compilerStack(_readFile), m_readFile(_readFile) { } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d3a6aa45dc24..7455cbca9b83 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -40,7 +40,7 @@ namespace solidity class Parser::ASTNodeFactory { public: - ASTNodeFactory(Parser const& _parser): + explicit ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.position(), -1, _parser.sourceName()) {} ASTNodeFactory(Parser const& _parser, ASTPointer const& _childNode): m_parser(_parser), m_location(_childNode->location()) {} @@ -69,7 +69,7 @@ class Parser::ASTNodeFactory class Parser::RecursionGuard { public: - RecursionGuard(Parser& _parser): + explicit RecursionGuard(Parser& _parser): m_parser(_parser) { m_parser.increaseRecursionDepth(); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5e6f3ef6b7ab..0f74880cf22f 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -35,7 +35,7 @@ class Scanner; class Parser: public ParserBase { public: - Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} + explicit Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} ASTPointer parse(std::shared_ptr const& _scanner); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index 5b03ab5e9920..48733fc1c00a 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -36,7 +36,7 @@ class Scanner; class ParserBase { public: - ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} + explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} std::shared_ptr const& sourceName() const; diff --git a/test/RPCSession.h b/test/RPCSession.h index f7fdd86ed879..558cb99f25be 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -124,7 +124,7 @@ class RPCSession: public boost::noncopyable std::string const& accountCreateIfNotExists(size_t _id); private: - RPCSession(std::string const& _path); + explicit RPCSession(std::string const& _path); inline std::string quote(std::string const& _arg) { return "\"" + _arg + "\""; } /// Parse std::string replacing keywords to values From 9897c56b2cacf162f8fd41d60e91b7f71863f8d5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 22:05:24 +0100 Subject: [PATCH 113/163] Mark a lot of functions static (where possible) --- libevmasm/ConstantOptimiser.cpp | 4 ++-- libevmasm/ConstantOptimiser.h | 4 ++-- liblll/CodeFragment.h | 4 ++-- libsolidity/ast/ASTJsonConverter.h | 6 +++--- libsolidity/codegen/ABIFunctions.cpp | 2 +- libsolidity/codegen/ABIFunctions.h | 2 +- libsolidity/inlineasm/AsmCodeGen.cpp | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index a7a87c35b677..2efd2dc9c4e6 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -159,7 +159,7 @@ AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const return actualCopyRoutine; } -AssemblyItems const& CodeCopyMethod::copyRoutine() const +AssemblyItems const& CodeCopyMethod::copyRoutine() { AssemblyItems static copyRoutine{ u256(0), @@ -234,7 +234,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) } } -bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const +bool ComputeMethod::checkRepresentation(u256 const& _value, AssemblyItems const& _routine) { // This is a tiny EVM that can only evaluate some instructions. vector stack; diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 19f14f7a076a..82982e25849b 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -121,7 +121,7 @@ class CodeCopyMethod: public ConstantOptimisationMethod virtual AssemblyItems execute(Assembly& _assembly) const override; protected: - AssemblyItems const& copyRoutine() const; + static AssemblyItems const& copyRoutine(); }; /** @@ -151,7 +151,7 @@ class ComputeMethod: public ConstantOptimisationMethod /// Tries to recursively find a way to compute @a _value. AssemblyItems findRepresentation(u256 const& _value); /// Recomputes the value from the calculated representation and checks for correctness. - bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const; + static bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine); bigint gasNeeded(AssemblyItems const& _routine) const; /// Counter for the complexity of optimization, will stop when it reaches zero. diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index 6622de3e7978..95d21563fe6e 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -50,8 +50,8 @@ class CodeFragment private: void finalise(CompilerState const& _cs); - template void error() const { BOOST_THROW_EXCEPTION(T() ); } - template void error(std::string const& reason) const { + template static void error() { BOOST_THROW_EXCEPTION(T() ); } + template static void error(std::string const& reason) { auto err = T(); err << errinfo_comment(reason); BOOST_THROW_EXCEPTION(err); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index cc589d4a42fc..186607cef7ec 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -119,7 +119,7 @@ class ASTJsonConverter: public ASTConstVisitor ); std::string sourceLocationToString(SourceLocation const& _location) const; std::string namePathToString(std::vector const& _namePath) const; - Json::Value idOrNull(ASTNode const* _pt) const + static Json::Value idOrNull(ASTNode const* _pt) { return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue; } @@ -134,12 +134,12 @@ class ASTJsonConverter: public ASTConstVisitor std::string literalTokenKind(Token::Value _token); std::string type(Expression const& _expression); std::string type(VariableDeclaration const& _varDecl); - int nodeId(ASTNode const& _node) const + static int nodeId(ASTNode const& _node) { return _node.id(); } template - Json::Value getContainerIds(Container const& container) const + static Json::Value getContainerIds(Container const& container) { Json::Value tmp(Json::arrayValue); for (auto const& element: container) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 2313473f55cf..a2938ed796e9 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -1056,7 +1056,7 @@ string ABIFunctions::createFunction(string const& _name, function con return _name; } -size_t ABIFunctions::headSize(TypePointers const& _targetTypes) const +size_t ABIFunctions::headSize(TypePointers const& _targetTypes) { size_t headSize = 0; for (auto const& t: _targetTypes) diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 103df5aee441..e43e2323ae85 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -162,7 +162,7 @@ class ABIFunctions std::string createFunction(std::string const& _name, std::function const& _creator); /// @returns the size of the static part of the encoding of the given types. - size_t headSize(TypePointers const& _targetTypes) const; + static size_t headSize(TypePointers const& _targetTypes); /// Map from function name to code for a multi-use function. std::map m_requestedFunctions; diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 24aa4ba8dc77..6d0c02558475 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -127,7 +127,7 @@ class EthAssemblyAdapter: public julia::AbstractAssembly } private: - LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const + static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) { u256 id = _tag.data(); solAssert(id <= std::numeric_limits::max(), "Tag id too large."); From 7b0046a9aafffec4e42be7e30c283e07ca1841b9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 16:43:15 +0200 Subject: [PATCH 114/163] Check inheritance specifier arguments for interfaces. --- libsolidity/analysis/TypeChecker.cpp | 10 +++++----- test/libsolidity/SolidityNameAndTypeResolution.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f70b75d3d992..99f3c64cbe75 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -429,12 +429,12 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) if (base->isLibrary()) m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); - // Interface can have no constructors - no need to validate - if (base->contractKind() == ContractDefinition::ContractKind::Interface) - return; - auto const& arguments = _inheritance.arguments(); - TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + TypePointers parameterTypes; + if (base->contractKind() != ContractDefinition::ContractKind::Interface) + // Interfaces do not have constructors, so there are zero parameters. + parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); + if (!arguments.empty() && parameterTypes.size() != arguments.size()) { m_errorReporter.typeError( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a4fc9c9864a0..f5f607ca0af2 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6735,6 +6735,15 @@ BOOST_AUTO_TEST_CASE(accept_library_creation) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(reject_interface_constructors) +{ + char const* text = R"( + interface I {} + contract C is I(2) {} + )"; + CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); +} + BOOST_AUTO_TEST_SUITE_END() } From 97cb571ba49b81bd20b840e20f27c2cf55730d81 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 12:29:04 +0200 Subject: [PATCH 115/163] Tests for recursion in JULIA. --- test/libjulia/Parser.cpp | 12 ++++++++++++ test/libsolidity/InlineAssembly.cpp | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index e1bf5a3a3abd..bfe764612317 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -237,6 +237,18 @@ BOOST_AUTO_TEST_CASE(builtin_types) BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }")); } +BOOST_AUTO_TEST_CASE(recursion_depth) +{ + string input; + for (size_t i = 0; i < 20000; i++) + input += "{"; + input += "let x:u256 := 0:u256"; + for (size_t i = 0; i < 20000; i++) + input += "}"; + + CHECK_ERROR(input, ParserError, "recursio"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8e1c304acd6b..0debc66d8828 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -400,6 +400,20 @@ BOOST_AUTO_TEST_CASE(instruction_too_many_arguments) CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (\"mul\" expects 2 arguments)"); } +BOOST_AUTO_TEST_CASE(recursion_depth) +{ + string input; + for (size_t i = 0; i < 20000; i++) + input += "{"; + input += "let x := 0"; + for (size_t i = 0; i < 20000; i++) + input += "}"; + + CHECK_PARSE_ERROR(input, ParserError, "recursion"); +} + + + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Printing) From 692e4c57e83607f21d0c1b1b735585b3b63564f3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 12:33:29 +0200 Subject: [PATCH 116/163] Check recursion depth in assembly parser. --- Changelog.md | 1 + libsolidity/inlineasm/AsmParser.cpp | 11 ++++++++++ libsolidity/parsing/Parser.cpp | 32 ----------------------------- libsolidity/parsing/Parser.h | 7 ------- libsolidity/parsing/ParserBase.cpp | 13 ++++++++++++ libsolidity/parsing/ParserBase.h | 20 ++++++++++++++++++ test/libjulia/Parser.cpp | 2 +- 7 files changed, 46 insertions(+), 40 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3574455b73c7..962aae2e5e3d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Features: Bugfixes: * Assembly Parser: Be more strict about number literals. + * Assembly Parser: Limit maximum recursion depth. * Parser: Enforce commas between array and tuple elements. * Parser: Limit maximum recursion depth. * Type Checker: Crash fix related to ``using``. diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 1dcc42b808e0..d84fe999c929 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -36,6 +36,7 @@ using namespace dev::solidity::assembly; shared_ptr Parser::parse(std::shared_ptr const& _scanner) { + m_recursionDepth = 0; try { m_scanner = _scanner; @@ -51,6 +52,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scann assembly::Block Parser::parseBlock() { + RecursionGuard recursionGuard(*this); assembly::Block block = createWithLocation(); expectToken(Token::LBrace); while (currentToken() != Token::RBrace) @@ -62,6 +64,7 @@ assembly::Block Parser::parseBlock() assembly::Statement Parser::parseStatement() { + RecursionGuard recursionGuard(*this); switch (currentToken()) { case Token::Let: @@ -158,6 +161,7 @@ assembly::Statement Parser::parseStatement() assembly::Case Parser::parseCase() { + RecursionGuard recursionGuard(*this); assembly::Case _case = createWithLocation(); if (m_scanner->currentToken() == Token::Default) m_scanner->next(); @@ -178,6 +182,7 @@ assembly::Case Parser::parseCase() assembly::ForLoop Parser::parseForLoop() { + RecursionGuard recursionGuard(*this); ForLoop forLoop = createWithLocation(); expectToken(Token::For); forLoop.pre = parseBlock(); @@ -192,6 +197,7 @@ assembly::ForLoop Parser::parseForLoop() assembly::Statement Parser::parseExpression() { + RecursionGuard recursionGuard(*this); Statement operation = parseElementaryOperation(true); if (operation.type() == typeid(Instruction)) { @@ -254,6 +260,7 @@ std::map const& Parser::instructionNames() assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) { + RecursionGuard recursionGuard(*this); Statement ret; switch (currentToken()) { @@ -342,6 +349,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) assembly::VariableDeclaration Parser::parseVariableDeclaration() { + RecursionGuard recursionGuard(*this); VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); while (true) @@ -366,6 +374,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() assembly::FunctionDefinition Parser::parseFunctionDefinition() { + RecursionGuard recursionGuard(*this); FunctionDefinition funDef = createWithLocation(); expectToken(Token::Function); funDef.name = expectAsmIdentifier(); @@ -397,6 +406,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) { + RecursionGuard recursionGuard(*this); if (_instruction.type() == typeid(Instruction)) { solAssert(!m_julia, "Instructions are invalid in JULIA"); @@ -479,6 +489,7 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) TypedName Parser::parseTypedName() { + RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation(); typedName.name = expectAsmIdentifier(); if (m_julia) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 7455cbca9b83..1ab38f82391c 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -64,25 +64,6 @@ class Parser::ASTNodeFactory SourceLocation m_location; }; -/// Utility class that creates an error and throws an exception if the -/// recursion depth is too deep. -class Parser::RecursionGuard -{ -public: - explicit RecursionGuard(Parser& _parser): - m_parser(_parser) - { - m_parser.increaseRecursionDepth(); - } - ~RecursionGuard() - { - m_parser.decreaseRecursionDepth(); - } - -private: - Parser& m_parser; -}; - ASTPointer Parser::parse(shared_ptr const& _scanner) { try @@ -1542,19 +1523,6 @@ ASTPointer Parser::createEmptyParameterList() return nodeFactory.createNode(vector>()); } -void Parser::increaseRecursionDepth() -{ - m_recursionDepth++; - if (m_recursionDepth >= 4096) - fatalParserError("Maximum recursion depth reached during parsing."); -} - -void Parser::decreaseRecursionDepth() -{ - solAssert(m_recursionDepth > 0, ""); - m_recursionDepth--; -} - string Parser::currentTokenName() { Token::Value token = m_scanner->currentToken(); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 0f74880cf22f..cfdfea7e2297 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -41,7 +41,6 @@ class Parser: public ParserBase private: class ASTNodeFactory; - class RecursionGuard; struct VarDeclParserOptions { @@ -165,14 +164,8 @@ class Parser: public ParserBase /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); - /// Increases the recursion depth and throws an exception if it is too deep. - void increaseRecursionDepth(); - void decreaseRecursionDepth(); - /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier. bool m_insideModifier = false; - /// Current recursion depth during parsing. - size_t m_recursionDepth = 0; }; } diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index 5657c2c02fbb..db988739dba5 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -101,6 +101,19 @@ void ParserBase::expectToken(Token::Value _value) m_scanner->next(); } +void ParserBase::increaseRecursionDepth() +{ + m_recursionDepth++; + if (m_recursionDepth >= 4096) + fatalParserError("Maximum recursion depth reached during parsing."); +} + +void ParserBase::decreaseRecursionDepth() +{ + solAssert(m_recursionDepth > 0, ""); + m_recursionDepth--; +} + void ParserBase::parserError(string const& _description) { m_errorReporter.parserError(SourceLocation(position(), position(), sourceName()), _description); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index 48733fc1c00a..fd0de0d10eef 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -41,6 +41,20 @@ class ParserBase std::shared_ptr const& sourceName() const; protected: + /// Utility class that creates an error and throws an exception if the + /// recursion depth is too deep. + class RecursionGuard + { + public: + explicit RecursionGuard(ParserBase& _parser): m_parser(_parser) + { + m_parser.increaseRecursionDepth(); + } + ~RecursionGuard() { m_parser.decreaseRecursionDepth(); } + private: + ParserBase& m_parser; + }; + /// Start position of the current token int position() const; /// End position of the current token @@ -56,6 +70,10 @@ class ParserBase Token::Value advance(); ///@} + /// Increases the recursion depth and throws an exception if it is too deep. + void increaseRecursionDepth(); + void decreaseRecursionDepth(); + /// Creates a @ref ParserError and annotates it with the current position and the /// given @a _description. void parserError(std::string const& _description); @@ -67,6 +85,8 @@ class ParserBase std::shared_ptr m_scanner; /// The reference to the list of errors and warning to add errors/warnings during parsing ErrorReporter& m_errorReporter; + /// Current recursion depth during parsing. + size_t m_recursionDepth = 0; }; } diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index bfe764612317..51070370817e 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(recursion_depth) for (size_t i = 0; i < 20000; i++) input += "}"; - CHECK_ERROR(input, ParserError, "recursio"); + CHECK_ERROR(input, ParserError, "recursion"); } BOOST_AUTO_TEST_SUITE_END() From 628b54ce351dd5de8ec34aa8c2c23f2fd0a77d90 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 22 Aug 2017 12:32:15 +0200 Subject: [PATCH 117/163] Reduce max recursion depth. --- libsolidity/parsing/ParserBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index db988739dba5..fe95b0feea98 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -104,7 +104,7 @@ void ParserBase::expectToken(Token::Value _value) void ParserBase::increaseRecursionDepth() { m_recursionDepth++; - if (m_recursionDepth >= 4096) + if (m_recursionDepth >= 3000) fatalParserError("Maximum recursion depth reached during parsing."); } From f38429fef88b644e9c54c1bf2a1abe754dbd324d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 22 Aug 2017 13:55:01 +0100 Subject: [PATCH 118/163] Switch stream properties back after temporary modification --- libdevcore/FixedHash.h | 2 ++ libevmasm/AssemblyItem.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 1c37b675ae5c..141e9ffd0755 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "CommonData.h" namespace dev @@ -224,6 +225,7 @@ template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& va template inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) { + boost::io::ios_all_saver guard(_out); _out << std::noshowbase << std::hex << std::setfill('0'); for (unsigned i = 0; i < N; ++i) _out << std::setw(2) << (int)_h[i]; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 7610486668ce..419a8c0b1bb8 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -219,10 +219,10 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << "\t" << _item.getJumpTypeAsString(); break; case Push: - _out << " PUSH " << hex << _item.data(); + _out << " PUSH " << hex << _item.data() << dec; break; case PushString: - _out << " PushString" << hex << (unsigned)_item.data(); + _out << " PushString" << hex << (unsigned)_item.data() << dec; break; case PushTag: { @@ -237,19 +237,19 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << " Tag " << _item.data(); break; case PushData: - _out << " PushData " << hex << (unsigned)_item.data(); + _out << " PushData " << hex << (unsigned)_item.data() << dec; break; case PushSub: - _out << " PushSub " << hex << size_t(_item.data()); + _out << " PushSub " << hex << size_t(_item.data()) << dec; break; case PushSubSize: - _out << " PushSubSize " << hex << size_t(_item.data()); + _out << " PushSubSize " << hex << size_t(_item.data()) << dec; break; case PushProgramSize: _out << " PushProgramSize"; break; case PushLibraryAddress: - _out << " PushLibraryAddress " << hex << h256(_item.data()).abridgedMiddle(); + _out << " PushLibraryAddress " << hex << h256(_item.data()).abridgedMiddle() << dec; break; case UndefinedItem: _out << " ???"; From f2d6226718a226ae4764bd7d1379c6e50c7f269a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 22 Aug 2017 13:55:28 +0100 Subject: [PATCH 119/163] Some smaller issues found by Covertiy Scan --- libevmasm/CommonSubexpressionEliminator.cpp | 1 + libevmasm/CommonSubexpressionEliminator.h | 2 +- libsolidity/ast/AST.cpp | 1 - libsolidity/ast/Types.cpp | 8 ++++---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 70324e7f5d44..293cb02cdc3f 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -220,6 +220,7 @@ void CSECodeGenerator::addDependencies(Id _c) if (m_neededBy.count(_c)) return; // we already computed the dependencies for _c ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); + assertThrow(expr.item, OptimizerException, ""); if (expr.item->type() == UndefinedItem) BOOST_THROW_EXCEPTION( // If this exception happens, we need to find a different way to generate the diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index f247320017dd..0b957a0ec3bf 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -147,7 +147,7 @@ class CSECodeGenerator AssemblyItems m_generatedItems; /// Current height of the stack relative to the start. - int m_stackHeight; + int m_stackHeight = 0; /// If (b, a) is in m_requests then b is needed to compute a. std::multimap m_neededBy; /// Current content of the stack. diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index e173237e061b..605daf557db0 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -188,7 +188,6 @@ vector, FunctionTypePointer>> const& ContractDefinition::inter { if (!m_interfaceFunctionList) { - set functionsSeen; set signaturesSeen; m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); for (ContractDefinition const* contract: annotation().linearizedBaseContracts) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index adf41229a536..5e61cdee7fe6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -739,18 +739,18 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == Category::Integer) { - auto targetType = dynamic_cast(&_convertTo); if (m_value == rational(0)) return true; if (isFractional()) return false; - int forSignBit = (targetType->isSigned() ? 1 : 0); + IntegerType const& targetType = dynamic_cast(_convertTo); + int forSignBit = (targetType.isSigned() ? 1 : 0); if (m_value > rational(0)) { - if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit))) return true; } - else if (targetType->isSigned() && -m_value.numerator() <= (u256(1) << (targetType->numBits() - forSignBit))) + else if (targetType.isSigned() && -m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit))) return true; return false; } From 9b6caa3fc425b2892562f16ae491d8f76d1fab26 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 22 Aug 2017 15:03:51 +0200 Subject: [PATCH 120/163] Remove visits to abstract AST class TypeName. --- libsolidity/ast/AST.h | 5 ++--- libsolidity/ast/ASTJsonConverter.cpp | 6 ------ 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 | 12 ------------ 7 files changed, 2 insertions(+), 40 deletions(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 53a34d32e7ab..4592a190796b 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -836,11 +836,10 @@ class MagicVariableDeclaration: public Declaration */ class TypeName: public ASTNode { -public: +protected: explicit TypeName(SourceLocation const& _location): ASTNode(_location) {} - virtual void accept(ASTVisitor& _visitor) override; - virtual void accept(ASTConstVisitor& _visitor) const override; +public: virtual TypeNameAnnotation& annotation() const override; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 3f16db232ca0..f76613578b45 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -378,12 +378,6 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node) return false; } -bool ASTJsonConverter::visit(TypeName const&) -{ - solAssert(false, "AST node of abstract type used."); - return false; -} - bool ASTJsonConverter::visit(EventDefinition const& _node) { m_inEvent = true; diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 186607cef7ec..380ae2c05c3c 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -73,7 +73,6 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; - bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index bcb5e3b9dc2e..392179ef5ae8 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -143,13 +143,6 @@ bool ASTPrinter::visit(EventDefinition const& _node) return goDeeper(); } -bool ASTPrinter::visit(TypeName const& _node) -{ - writeLine("TypeName"); - printSourcePart(_node); - return goDeeper(); -} - bool ASTPrinter::visit(ElementaryTypeName const& _node) { writeLine(string("ElementaryTypeName ") + _node.typeName().toString()); @@ -434,11 +427,6 @@ void ASTPrinter::endVisit(EventDefinition const&) m_indentation--; } -void ASTPrinter::endVisit(TypeName const&) -{ - m_indentation--; -} - void ASTPrinter::endVisit(ElementaryTypeName const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 193f929663d8..d6897dfd502f 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -60,7 +60,6 @@ class ASTPrinter: public ASTConstVisitor bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; - bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; @@ -104,7 +103,6 @@ class ASTPrinter: public ASTConstVisitor void endVisit(ModifierDefinition const&) override; void endVisit(ModifierInvocation const&) override; void endVisit(EventDefinition const&) override; - void endVisit(TypeName const&) override; void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; void endVisit(FunctionTypeName const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 20be634b58c2..b726d592dc16 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -58,7 +58,6 @@ class ASTVisitor virtual bool visit(ModifierDefinition& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); } virtual bool visit(EventDefinition& _node) { return visitNode(_node); } - virtual bool visit(TypeName& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName& _node) { return visitNode(_node); } @@ -104,7 +103,6 @@ class ASTVisitor virtual void endVisit(ModifierDefinition& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition& _node) { endVisitNode(_node); } - virtual void endVisit(TypeName& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName& _node) { endVisitNode(_node); } @@ -162,7 +160,6 @@ class ASTConstVisitor virtual bool visit(ModifierDefinition const& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation const& _node) { return visitNode(_node); } virtual bool visit(EventDefinition const& _node) { return visitNode(_node); } - virtual bool visit(TypeName const& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName const& _node) { return visitNode(_node); } @@ -208,7 +205,6 @@ class ASTConstVisitor virtual void endVisit(ModifierDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition const& _node) { endVisitNode(_node); } - virtual void endVisit(TypeName const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 7c1c64b09b5a..904d9ff65475 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -291,18 +291,6 @@ void EventDefinition::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } -void TypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void TypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void ElementaryTypeName::accept(ASTVisitor& _visitor) { _visitor.visit(*this); From 15fd43f0357e1f3c6ec1262df6ee39b06a6fe3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 22 Aug 2017 16:37:27 +0200 Subject: [PATCH 121/163] CMake: Fix linking with pthread --- CMakeLists.txt | 2 ++ libdevcore/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a11c56ee6bdf..d204ffec3d1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ project(solidity VERSION ${PROJECT_VERSION}) include(EthDependencies) include(deps/jsoncpp.cmake) +find_package(Threads) + # Figure out what compiler and system are we using include(EthCompilerSettings) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 4b15427f9cc8..016d7ea33fbd 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) +target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} Threads::Threads) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories(devcore PRIVATE ..) add_dependencies(devcore solidity_BuildInfo.h) From efe4d68a7bdc55fd79b9e01b863064b27528428a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 8 Sep 2016 02:18:17 +0100 Subject: [PATCH 122/163] Introduce view (and keep constant as an alias) --- Changelog.md | 1 + docs/types.rst | 2 +- libsolidity/ast/ASTJsonConverter.cpp | 2 ++ libsolidity/ast/Types.h | 1 - libsolidity/interface/ABI.cpp | 3 ++- libsolidity/parsing/Parser.cpp | 3 ++- libsolidity/parsing/Token.h | 4 ++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 6 +++--- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index 39cbd44bec17..efcea03f2acf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. * Parser: Display previous visibility specifier in error if multiple are found. + * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. * Static Analyzer: Warn about large storage structures. * Metadata: Store experimental flag in metadata CBOR. diff --git a/docs/types.rst b/docs/types.rst index 13c848c26d71..9c1c73c67da4 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -337,7 +337,7 @@ be passed via and returned from external function calls. Function types are notated as follows:: - function () {internal|external} [constant|payable] [returns ()] + function () {internal|external} [constant|view|payable] [returns ()] In contrast to the parameter types, the return types cannot be empty - if the function type should not return anything, the whole ``returns ()`` diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f76613578b45..cb74ea397981 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -323,6 +323,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector> attributes = { make_pair("name", _node.name()), + // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), @@ -415,6 +416,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), + // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 56546a820ecd..ce2d3bf8cee4 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -993,7 +993,6 @@ class FunctionType: public Type return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - bool isConstant() const { return m_stateMutability == StateMutability::View; } /// @returns true if the the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index a7224e86a0f9..6e8563f7b160 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -35,7 +35,8 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value method; method["type"] = "function"; method["name"] = it.second->declaration().name(); - method["constant"] = it.second->isConstant(); + // TODO: deprecate constant in a future release + method["constant"] = it.second->stateMutability() == StateMutability::View; method["payable"] = it.second->isPayable(); method["statemutability"] = stateMutabilityToString(it.second->stateMutability()); method["inputs"] = formatTypeList( diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 7455cbca9b83..92a614e04477 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -337,7 +337,8 @@ StateMutability Parser::parseStateMutability(Token::Value _token) StateMutability stateMutability(StateMutability::NonPayable); if (_token == Token::Payable) stateMutability = StateMutability::Payable; - else if (_token == Token::Constant) + // FIXME: constant should be removed at the next breaking release + else if (_token == Token::View || _token == Token::Constant) stateMutability = StateMutability::View; else solAssert(false, "Invalid state mutability specifier."); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index efbe5e9e1480..3bc52f1d5389 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -176,6 +176,7 @@ namespace solidity K(Throw, "throw", 0) \ K(Using, "using", 0) \ K(Var, "var", 0) \ + K(View, "view", 0) \ K(While, "while", 0) \ \ /* Ether subdenominations */ \ @@ -236,7 +237,6 @@ namespace solidity K(Try, "try", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ - K(View, "view", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -290,7 +290,7 @@ class Token static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } - static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; } + static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == View || op == Payable; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f5f607ca0af2..380978e812be 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1172,7 +1172,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_REQUIRE(function && function->hasDeclaration()); auto returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "uint256"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "map(uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); @@ -1180,7 +1180,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_EQUAL(params.at(0)->canonicalName(false), "uint256"); returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); function = retrieveFunctionBySignature(*contract, "multiple_map(uint256,uint256)"); BOOST_REQUIRE(function && function->hasDeclaration()); @@ -1189,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK_EQUAL(params.at(1)->canonicalName(false), "uint256"); returnParams = function->returnParameterTypes(); BOOST_CHECK_EQUAL(returnParams.at(0)->canonicalName(false), "bytes4"); - BOOST_CHECK(function->isConstant()); + BOOST_CHECK(function->stateMutability() == StateMutability::View); } BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) From 9e8d2a561f79be15f3ebdbdd139835b50baa43ce Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 17 Nov 2016 20:38:50 +0000 Subject: [PATCH 123/163] Update grammar with view --- docs/grammar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 76827a773728..36ba36f0081b 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -53,7 +53,7 @@ ArrayTypeName = TypeName '[' Expression? ']' FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )* ( 'returns' TypeNameList )? StorageLocation = 'memory' | 'storage' -StateMutability = 'constant' | 'payable' +StateMutability = 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | From 70bb1e74788b7c2938ab15e14ee048cf9a1a7743 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 23:21:27 +0100 Subject: [PATCH 124/163] Update tests for view --- test/libsolidity/SolidityABIJSON.cpp | 58 +++++++++++++++++++++++++++- test/libsolidity/SolidityParser.cpp | 5 +++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 80b4b6ad2012..12fb1f9c185a 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -250,7 +250,63 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) checkInterface(sourceCode, interface); } -BOOST_AUTO_TEST_CASE(const_function) +BOOST_AUTO_TEST_CASE(view_function) +{ + char const* sourceCode = R"( + contract test { + function foo(uint a, uint b) returns(uint d) { return a + b; } + function boo(uint32 a) view returns(uint b) { return a * 4; } + } + )"; + + char const* interface = R"([ + { + "name": "foo", + "constant": false, + "payable" : false, + "statemutability": "nonpayable", + "type": "function", + "inputs": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + }, + { + "name": "boo", + "constant": true, + "payable" : false, + "statemutability": "view", + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint32" + }], + "outputs": [ + { + "name": "b", + "type": "uint256" + } + ] + } + ])"; + + checkInterface(sourceCode, interface); +} + +// constant is an alias to view above +BOOST_AUTO_TEST_CASE(constant_function) { char const* sourceCode = R"( contract test { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 30dc80d95d61..8e84ead1789e 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -918,6 +918,11 @@ BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers) function f() constant constant {} })"; CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\"."); + text = R"( + contract c { + function f() constant view {} + })"; + CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\"."); text = R"( contract c { function f() payable constant {} From b1cdf81506de39502db0fb4a4b55ba8155f853ab Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 21 Aug 2017 23:41:46 +0100 Subject: [PATCH 125/163] Document view functions --- docs/abi-spec.rst | 2 +- docs/contracts.rst | 19 +++++++++++-------- docs/miscellaneous.rst | 5 +++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index a9110a0af705..159bd6c7ce5c 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -293,7 +293,7 @@ The JSON format for a contract's interface is given by an array of function and/ * `name`: the name of the parameter; * `type`: the canonical type of the parameter. - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; -- `constant`: `true` if function is :ref:`specified to not modify blockchain state `); +- `constant`: `true` if function is :ref:`specified to not modify blockchain state `); - `payable`: `true` if function accepts ether, defaults to `false`; - `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). diff --git a/docs/contracts.rst b/docs/contracts.rst index 7b972e17bed3..0f1a882c69e0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -461,29 +461,32 @@ value types and strings. } -.. _constant-functions: +.. _view-functions: -****************** -Constant Functions -****************** +************** +View Functions +************** -Functions can be declared constant in which case they promise not to modify the state. +Functions can be declared ``view`` in which case they promise not to modify the state. :: pragma solidity ^0.4.0; contract C { - function f(uint a, uint b) constant returns (uint) { + function f(uint a, uint b) view returns (uint) { return a * (b + 42); } } .. note:: - Getter methods are marked constant. + ``constant`` is an alias to ``view``. + +.. note:: + Getter methods are marked ``view``. .. warning:: - The compiler does not enforce yet that a constant method is not modifying state. + The compiler does not enforce yet that a ``view`` method is not modifying state. .. index:: ! fallback function, function;fallback diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 199182d381d9..7889fff2d0e0 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -505,11 +505,12 @@ Function Visibility Specifiers Modifiers ========= +- ``view`` for functions: Disallow modification of state - this is not enforced yet. +- ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. -- ``constant`` for functions: Disallows modification of state - this is not enforced yet. +- ``constant`` for functions: Same as ``view``. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. -- ``payable`` for functions: Allows them to receive Ether together with a call. Reserved Keywords ================= From 4974ff3962759a19150877b0e59e64319190ba1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 22 Aug 2017 22:50:03 +0200 Subject: [PATCH 126/163] CMake: Add option to static link solc --- CMakeLists.txt | 2 ++ scripts/Dockerfile | 2 +- solc/CMakeLists.txt | 14 +++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d204ffec3d1d..da14ddb77135 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ eth_policy() set(PROJECT_VERSION "0.4.16") project(solidity VERSION ${PROJECT_VERSION}) +option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) + # Let's find our dependencies include(EthDependencies) include(deps/jsoncpp.cmake) diff --git a/scripts/Dockerfile b/scripts/Dockerfile index c984ce99d968..654a9f29a03d 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -9,7 +9,7 @@ COPY / $WORKDIR #Install dependencies, eliminate annoying warnings, and build release, delete all remaining points and statically link. RUN ./scripts/install_deps.sh && sed -i -E -e 's/include /include /' /usr/include/boost/asio/detail/socket_types.hpp &&\ -cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSTATIC_LINKING=1 &&\ +cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSOLC_LINK_STATIC=1 &&\ make solc && install -s solc/solc /usr/bin &&\ cd / && rm -rf solidity &&\ apk del sed build-base git make cmake gcc g++ musl-dev curl-dev boost-dev &&\ diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 0563fe2aa401..656b27c34315 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -16,4 +16,16 @@ if (EMSCRIPTEN) else() add_library(soljson jsonCompiler.cpp) endif() -target_link_libraries(soljson PRIVATE solidity) \ No newline at end of file +target_link_libraries(soljson PRIVATE solidity) + +if(SOLC_LINK_STATIC AND UNIX AND NOT APPLE) + # Produce solc as statically linked binary (includes C/C++ standard libraries) + # This is not supported on macOS, see + # https://developer.apple.com/library/content/qa/qa1118/_index.html. + set_target_properties( + solc PROPERTIES + LINK_FLAGS -static + LINK_SEARCH_START_STATIC ON + LINK_SEARCH_END_STATIC ON + ) +endif() \ No newline at end of file From 388fc983fa5480ec8498c399cf598991c63cfa22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 22 Aug 2017 16:43:30 +0200 Subject: [PATCH 127/163] CMake: Cleanup "local" include path --- libdevcore/CMakeLists.txt | 2 +- libevmasm/CMakeLists.txt | 1 - liblll/CMakeLists.txt | 1 - libsolidity/CMakeLists.txt | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 016d7ea33fbd..b97866f476d2 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -4,5 +4,5 @@ file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} Threads::Threads) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) -target_include_directories(devcore PRIVATE ..) +target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") add_dependencies(devcore solidity_BuildInfo.h) diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt index 914339e74ff8..5c945c7d4688 100644 --- a/libevmasm/CMakeLists.txt +++ b/libevmasm/CMakeLists.txt @@ -3,4 +3,3 @@ file(GLOB headers "*.h") add_library(evmasm ${sources} ${headers}) target_link_libraries(evmasm PUBLIC devcore jsoncpp) -target_include_directories(evmasm PUBLIC ..) diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt index 1cc37da32c39..4cdc073a56b9 100644 --- a/liblll/CMakeLists.txt +++ b/liblll/CMakeLists.txt @@ -3,4 +3,3 @@ file(GLOB headers "*.h") add_library(lll ${sources} ${headers}) target_link_libraries(lll PUBLIC evmasm devcore) -target_include_directories(lll PUBLIC ..) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index fd27a492128c..11fd6fe27f56 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -4,4 +4,3 @@ file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") add_library(solidity ${sources} ${headers}) target_link_libraries(solidity PUBLIC evmasm devcore) -target_include_directories(solidity PUBLIC ..) From 09628dee8c86e71119884204eb729fe439fb3bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 22 Aug 2017 16:55:54 +0200 Subject: [PATCH 128/163] docs: Add a note about CMake options --- docs/installing-solidity.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index ddc5c850ac60..782bb606b391 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -230,6 +230,7 @@ Or, on Windows: Command-Line Build ------------------ +Solidity project uses CMake to configure the build. Building Solidity is quite similar on Linux, macOS and other Unices: .. code:: bash @@ -264,6 +265,11 @@ Alternatively, you can build for Windows on the command-line, like so: cmake --build . --config RelWithDebInfo +CMake options +============= + +If you are interested what CMake options are available run ``cmake .. -LH``. + The version string in detail ============================ From 1ece7bf4433a37feb449b7f3b4f820a1f5740eaf Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 6 Jul 2017 11:05:05 +0200 Subject: [PATCH 129/163] z3 conditions --- CMakeLists.txt | 1 + cmake/UseSolidity.cmake | 2 +- deps | 2 +- libsolidity/CMakeLists.txt | 2 +- libsolidity/ast/AST.cpp | 1 + libsolidity/formal/SMTChecker.cpp | 36 ++ libsolidity/formal/SMTChecker.h | 45 +++ libsolidity/formal/SMTCheckerImpl.cpp | 456 ++++++++++++++++++++++++ libsolidity/formal/SMTCheckerImpl.h | 99 +++++ libsolidity/interface/CompilerStack.cpp | 8 + 10 files changed, 649 insertions(+), 3 deletions(-) create mode 100644 libsolidity/formal/SMTChecker.cpp create mode 100644 libsolidity/formal/SMTChecker.h create mode 100644 libsolidity/formal/SMTCheckerImpl.cpp create mode 100644 libsolidity/formal/SMTCheckerImpl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da14ddb77135..29ca63fe0108 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" # Let's find our dependencies include(EthDependencies) include(deps/jsoncpp.cmake) +include(deps/z3.cmake) find_package(Threads) diff --git a/cmake/UseSolidity.cmake b/cmake/UseSolidity.cmake index 5080f71b4ae4..9e2dea3ea62c 100644 --- a/cmake/UseSolidity.cmake +++ b/cmake/UseSolidity.cmake @@ -25,7 +25,7 @@ function(eth_apply TARGET REQUIRED SUBMODULE) if (${SUBMODULE} STREQUAL "solidity" OR ${SUBMODULE} STREQUAL "") eth_use(${TARGET} ${REQUIRED} Dev::soldevcore Solidity::solevmasm) - target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES}) + target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES} z3) endif() target_compile_definitions(${TARGET} PUBLIC ETH_SOLIDITY) diff --git a/deps b/deps index e5c8316db8d3..cd9c6ff69134 160000 --- a/deps +++ b/deps @@ -1 +1 @@ -Subproject commit e5c8316db8d3daa0abc3b5af8545ce330057608c +Subproject commit cd9c6ff69134e8f53bde70d92cce12ad294752de diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 11fd6fe27f56..a88d16b8c06e 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -3,4 +3,4 @@ file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") add_library(solidity ${sources} ${headers}) -target_link_libraries(solidity PUBLIC evmasm devcore) +target_link_libraries(solidity PUBLIC evmasm devcore z3) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 605daf557db0..7f4dea0ef48b 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp new file mode 100644 index 000000000000..0e559fae5b79 --- /dev/null +++ b/libsolidity/formal/SMTChecker.cpp @@ -0,0 +1,36 @@ +/* + 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; + +SMTChecker::SMTChecker(ErrorReporter& _errorReporter): + m_impl(make_shared(_errorReporter)) +{ +} + +void SMTChecker::analyze(SourceUnit const& _source) +{ + m_impl->analyze(_source); +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h new file mode 100644 index 000000000000..7e7ee3d90cda --- /dev/null +++ b/libsolidity/formal/SMTChecker.h @@ -0,0 +1,45 @@ +/* + 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 ErrorReporter; +class SourceUnit; +class SMTCheckerImpl; + +class SMTChecker +{ +public: + SMTChecker(ErrorReporter& _errorReporter); + + void analyze(SourceUnit const& _sources); + +private: + std::shared_ptr m_impl; +}; + +} +} diff --git a/libsolidity/formal/SMTCheckerImpl.cpp b/libsolidity/formal/SMTCheckerImpl.cpp new file mode 100644 index 000000000000..0a9ad66e77d0 --- /dev/null +++ b/libsolidity/formal/SMTCheckerImpl.cpp @@ -0,0 +1,456 @@ +/* + 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; + +SMTCheckerImpl::SMTCheckerImpl(ErrorReporter& _errorReporter): + m_solver(m_context), + m_errorReporter(_errorReporter) +{ +} + +void SMTCheckerImpl::analyze(SourceUnit const& _source) +{ + bool pragmaFound = false; + for (auto const& node: _source.nodes()) + if (auto const* pragma = dynamic_cast(node.get())) + if (pragma->literals()[0] == "checkAssertionsZ3") + pragmaFound = true; + if (pragmaFound) + { + m_solver.reset(); + m_currentSequenceCounter.clear(); + _source.accept(*this); + } +} + +void SMTCheckerImpl::endVisit(VariableDeclaration const& _varDecl) +{ + if (_varDecl.value()) + { + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support this." + ); + } + else if (_varDecl.isLocalOrReturn()) + createVariable(_varDecl, true); + else if (_varDecl.isCallableParameter()) + createVariable(_varDecl, false); +} + +bool SMTCheckerImpl::visit(FunctionDefinition const& _function) +{ + if (!_function.modifiers().empty() || _function.isConstructor()) + m_errorReporter.warning( + _function.location(), + "Assertion checker does not yet support constructors and functions with modifiers." + ); + // TODO actually we probably also have to reset all local variables and similar things. + m_currentFunction = &_function; + m_solver.push(); + return true; +} + +void SMTCheckerImpl::endVisit(FunctionDefinition const&) +{ + // TOOD we could check for "reachability", i.e. satisfiability here. + m_solver.pop(); + m_currentFunction = nullptr; +} + +void SMTCheckerImpl::endVisit(VariableDeclarationStatement const& _varDecl) +{ + if (_varDecl.declarations().size() != 1) + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support such variable declarations." + ); + else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) + // TODO more checks? + // TODO add restrictions about type (might be assignment from smaller type) + m_solver.add(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + else + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet implement such variable declarations." + ); +} + +void SMTCheckerImpl::endVisit(ExpressionStatement const&) +{ +} + +void SMTCheckerImpl::endVisit(Assignment const& _assignment) +{ + if (_assignment.assignmentOperator() != Token::Value::Assign) + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement compound assignment." + ); + else if (_assignment.annotation().type->category() != Type::Category::Integer) + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() + ); + else if (Identifier const* identifier = dynamic_cast(&_assignment.leftHandSide())) + { + Declaration const* decl = identifier->annotation().referencedDeclaration; + if (knownVariable(*decl)) + // TODO more checks? + // TODO add restrictions about type (might be assignment from smaller type) + m_solver.add(newValue(*decl) == expr(_assignment.rightHandSide())); + else + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement such assignments." + ); + } + else + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement such assignments." + ); +} + +void SMTCheckerImpl::endVisit(TupleExpression const& _tuple) +{ + if (_tuple.isInlineArray() || _tuple.components().size() != 1) + m_errorReporter.warning( + _tuple.location(), + "Assertion checker does not yet implement tules and inline arrays." + ); + else + m_solver.add(expr(_tuple) == expr(*_tuple.components()[0])); +} + +void SMTCheckerImpl::endVisit(BinaryOperation const& _op) +{ + if (Token::isArithmeticOp(_op.getOperator())) + arithmeticOperation(_op); + else if (Token::isCompareOp(_op.getOperator())) + compareOperation(_op); + else if (Token::isBooleanOp(_op.getOperator())) + booleanOperation(_op); + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); +} + +void SMTCheckerImpl::endVisit(FunctionCall const& _funCall) +{ + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + + std::vector> const args = _funCall.arguments(); + if (funType.kind() == FunctionType::Kind::Assert) + { + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); + m_solver.add(expr(*args[0])); + } + else if (funType.kind() == FunctionType::Kind::Require) + { + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + m_solver.add(expr(*args[0])); + checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); + // TODO is there something meaningful we can check here? + // We can check whether the condition is always fulfilled or never fulfilled. + } +} + +void SMTCheckerImpl::endVisit(Identifier const& _identifier) +{ + Declaration const* decl = _identifier.annotation().referencedDeclaration; + solAssert(decl, ""); + if (dynamic_cast(_identifier.annotation().type.get())) + { + m_solver.add(expr(_identifier) == currentValue(*decl)); + return; + } + else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) + { + if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) + return; + // TODO for others, clear our knowledge about storage and memory + } + m_errorReporter.warning( + _identifier.location(), + "Assertion checker does not yet support the type of this expression (" + + _identifier.annotation().type->toString() + + ")." + ); +} + +void SMTCheckerImpl::endVisit(Literal const& _literal) +{ + Type const& type = *_literal.annotation().type; + if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber) + { + if (RationalNumberType const* rational = dynamic_cast(&type)) + solAssert(!rational->isFractional(), ""); + + m_solver.add(expr(_literal) == m_context.int_val(type.literalValue(&_literal).str().c_str())); + } + else + m_errorReporter.warning( + _literal.location(), + "Assertion checker does not yet support the type of this expression (" + + _literal.annotation().type->toString() + + ")." + ); +} + +void SMTCheckerImpl::arithmeticOperation(BinaryOperation const& _op) +{ + switch (_op.getOperator()) + { + case Token::Add: + case Token::Sub: + case Token::Mul: + { + solAssert(_op.annotation().commonType, ""); + solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); + z3::expr left(expr(_op.leftExpression())); + z3::expr right(expr(_op.rightExpression())); + Token::Value op = _op.getOperator(); + z3::expr value( + op == Token::Add ? left + right : + op == Token::Sub ? left - right : + /*op == Token::Mul*/ left * right + ); + + // Overflow check + auto const& intType = dynamic_cast(*_op.annotation().commonType); + checkCondition( + value < minValue(intType), + _op.location(), + "Underflow (resulting value less than " + intType.minValue().str() + ")", + "value", + &value + ); + checkCondition( + value > maxValue(intType), + _op.location(), + "Overflow (resulting value larger than " + intType.maxValue().str() + ")", + "value", + &value + ); + + m_solver.add(expr(_op) == value); + break; + } + default: + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); + } +} + +void SMTCheckerImpl::compareOperation(BinaryOperation const& _op) +{ + solAssert(_op.annotation().commonType, ""); + if (_op.annotation().commonType->category() == Type::Category::Integer) + { + z3::expr left(expr(_op.leftExpression())); + z3::expr right(expr(_op.rightExpression())); + Token::Value op = _op.getOperator(); + z3::expr 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) + ); + // TODO: check that other values for op are not possible. + m_solver.add(expr(_op) == value); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons" + ); +} + +void SMTCheckerImpl::booleanOperation(BinaryOperation const& _op) +{ + solAssert(_op.getOperator() == Token::And || _op.getOperator() == Token::Or, ""); + solAssert(_op.annotation().commonType, ""); + if (_op.annotation().commonType->category() == Type::Category::Bool) + { + if (_op.getOperator() == Token::And) + m_solver.add(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + else + m_solver.add(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations" + ); +} + +void SMTCheckerImpl::checkCondition( + z3::expr _condition, + SourceLocation const& _location, + string const& _description, + string const& _additionalValueName, + z3::expr* _additionalValue +) +{ + m_solver.push(); + m_solver.add(_condition); + switch (m_solver.check()) + { + case z3::check_result::sat: + { + std::ostringstream message; + message << _description << " happens here"; + if (m_currentFunction) + { + message << " for:\n"; + z3::model m = m_solver.get_model(); + if (_additionalValue) + message << " " << _additionalValueName << " = " << m.eval(*_additionalValue) << "\n"; + for (auto const& param: m_currentFunction->parameters()) + if (knownVariable(*param)) + message << " " << param->name() << " = " << m.eval(currentValue(*param)) << "\n"; + for (auto const& var: m_currentFunction->localVariables()) + if (knownVariable(*var)) + message << " " << var->name() << " = " << m.eval(currentValue(*var)) << "\n"; +// message << m << endl; +// message << m_solver << endl; + } + else + message << "."; + m_errorReporter.warning(_location, message.str()); + break; + } + case z3::check_result::unsat: + break; + case z3::check_result::unknown: + m_errorReporter.warning(_location, _description + " might happen here."); + break; + } + m_solver.pop(); +} + +void SMTCheckerImpl::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) +{ + if (auto intType = dynamic_cast(_varDecl.type().get())) + { + solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); + solAssert(m_z3Variables.count(&_varDecl) == 0, ""); + m_currentSequenceCounter[&_varDecl] = 0; + m_z3Variables.emplace(&_varDecl, m_context.function(uniqueSymbol(_varDecl).c_str(), m_context.int_sort(), m_context.int_sort())); + if (_setToZero) + m_solver.add(currentValue(_varDecl) == 0); + else + { + m_solver.add(currentValue(_varDecl) >= minValue(*intType)); + m_solver.add(currentValue(_varDecl) <= maxValue(*intType)); + } + } + else + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support the type of this variable." + ); +} + +string SMTCheckerImpl::uniqueSymbol(Declaration const& _decl) +{ + return _decl.name() + "_" + to_string(_decl.id()); +} + +string SMTCheckerImpl::uniqueSymbol(Expression const& _expr) +{ + return "expr_" + to_string(_expr.id()); +} + +bool SMTCheckerImpl::knownVariable(Declaration const& _decl) +{ + return m_currentSequenceCounter.count(&_decl); +} + +z3::expr SMTCheckerImpl::currentValue(Declaration const& _decl) +{ + solAssert(m_currentSequenceCounter.count(&_decl), ""); + return var(_decl)(m_currentSequenceCounter.at(&_decl)); +} + +z3::expr SMTCheckerImpl::newValue(const Declaration& _decl) +{ + solAssert(m_currentSequenceCounter.count(&_decl), ""); + m_currentSequenceCounter[&_decl]++; + return currentValue(_decl); +} + +z3::expr SMTCheckerImpl::minValue(IntegerType const& _t) +{ + return m_context.int_val(_t.minValue().str().c_str()); +} + +z3::expr SMTCheckerImpl::maxValue(IntegerType const& _t) +{ + return m_context.int_val(_t.maxValue().str().c_str()); +} + +z3::expr SMTCheckerImpl::expr(Expression const& _e) +{ + if (!m_z3Expressions.count(&_e)) + { + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) + { + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_z3Expressions.emplace(&_e, m_context.int_const(uniqueSymbol(_e).c_str())); + break; + } + case Type::Category::Integer: + m_z3Expressions.emplace(&_e, m_context.int_const(uniqueSymbol(_e).c_str())); + break; + case Type::Category::Bool: + m_z3Expressions.emplace(&_e, m_context.bool_const(uniqueSymbol(_e).c_str())); + break; + default: + solAssert(false, "Type not implemented."); + } + } + return m_z3Expressions.at(&_e); +} + +z3::func_decl SMTCheckerImpl::var(Declaration const& _decl) +{ + solAssert(m_z3Variables.count(&_decl), ""); + return m_z3Variables.at(&_decl); +} diff --git a/libsolidity/formal/SMTCheckerImpl.h b/libsolidity/formal/SMTCheckerImpl.h new file mode 100644 index 000000000000..a82ec92ab5cd --- /dev/null +++ b/libsolidity/formal/SMTCheckerImpl.h @@ -0,0 +1,99 @@ +/* + 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 +{ + +class ErrorReporter; + +class SMTCheckerImpl: private ASTConstVisitor +{ +public: + SMTCheckerImpl(ErrorReporter& _errorReporter); + + void analyze(SourceUnit const& _sources); + +private: + // TODO: Check that we do not have concurrent reads and writes to a variable, + // because the order of expression evaluation is undefined + // TODO: or just force a certain order, but people might have a different idea about that. + + virtual void endVisit(VariableDeclaration const& _node) override; + virtual bool visit(FunctionDefinition const& _node) override; + virtual void endVisit(FunctionDefinition const& _node) override; + virtual void endVisit(VariableDeclarationStatement const& _node) override; + virtual void endVisit(ExpressionStatement const& _node) override; + virtual void endVisit(Assignment const& _node) override; + virtual void endVisit(TupleExpression const& _node) override; + virtual void endVisit(BinaryOperation const& _node) override; + virtual void endVisit(FunctionCall const& _node) override; + virtual void endVisit(Identifier const& _node) override; + virtual void endVisit(Literal const& _node) override; + + void arithmeticOperation(BinaryOperation const& _op); + void compareOperation(BinaryOperation const& _op); + void booleanOperation(BinaryOperation const& _op); + + void checkCondition( + z3::expr _condition, + SourceLocation const& _location, + std::string const& _description, + std::string const& _additionalValueName = "", + z3::expr* _additionalValue = nullptr + ); + + void createVariable(VariableDeclaration const& _varDecl, bool _setToZero); + + std::string uniqueSymbol(Declaration const& _decl); + std::string uniqueSymbol(Expression const& _expr); + bool knownVariable(Declaration const& _decl); + z3::expr currentValue(Declaration const& _decl); + z3::expr newValue(Declaration const& _decl); + + z3::expr minValue(IntegerType const& _t); + z3::expr maxValue(IntegerType const& _t); + + /// Returns the z3 expression corresponding to the AST node. Creates a new expression + /// if it does not exist yet. + z3::expr expr(Expression const& _e); + /// Returns the z3 function declaration corresponding to the given variable. + /// The function takes one argument which is the "sequence number". + z3::func_decl var(Declaration const& _decl); + + z3::context m_context; + z3::solver m_solver; + std::map m_currentSequenceCounter; + std::map m_z3Expressions; + std::map m_z3Variables; + ErrorReporter& m_errorReporter; + + FunctionDefinition const* m_currentFunction = nullptr; +}; + +} +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 412d2fd37517..4283cd992740 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -236,6 +237,13 @@ bool CompilerStack::analyze() noErrors = false; } + if (noErrors) + { + SMTChecker smtChecker(m_errorReporter); + for (Source const* source: m_sourceOrder) + smtChecker.analyze(*source->ast); + } + if (noErrors) { m_stackState = AnalysisSuccessful; From df848859da0ce52a7bb1de6fcd836e0b7ebc2779 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 10 Jul 2017 18:13:38 +0200 Subject: [PATCH 130/163] Rewrite using SMTLIB2 interface. --- libsolidity/formal/SMTCheckerImpl.cpp | 96 ++++++++------- libsolidity/formal/SMTCheckerImpl.h | 26 ++-- libsolidity/formal/SMTLib2Interface.cpp | 24 ++++ libsolidity/formal/SMTLib2Interface.h | 150 ++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 63 deletions(-) create mode 100644 libsolidity/formal/SMTLib2Interface.cpp create mode 100644 libsolidity/formal/SMTLib2Interface.h diff --git a/libsolidity/formal/SMTCheckerImpl.cpp b/libsolidity/formal/SMTCheckerImpl.cpp index 0a9ad66e77d0..028e4400afa3 100644 --- a/libsolidity/formal/SMTCheckerImpl.cpp +++ b/libsolidity/formal/SMTCheckerImpl.cpp @@ -25,7 +25,6 @@ using namespace dev; using namespace dev::solidity; SMTCheckerImpl::SMTCheckerImpl(ErrorReporter& _errorReporter): - m_solver(m_context), m_errorReporter(_errorReporter) { } @@ -39,7 +38,7 @@ void SMTCheckerImpl::analyze(SourceUnit const& _source) pragmaFound = true; if (pragmaFound) { - m_solver.reset(); + m_interface.reset(); m_currentSequenceCounter.clear(); _source.accept(*this); } @@ -69,14 +68,14 @@ bool SMTCheckerImpl::visit(FunctionDefinition const& _function) ); // TODO actually we probably also have to reset all local variables and similar things. m_currentFunction = &_function; - m_solver.push(); + m_interface.push(); return true; } void SMTCheckerImpl::endVisit(FunctionDefinition const&) { // TOOD we could check for "reachability", i.e. satisfiability here. - m_solver.pop(); + m_interface.pop(); m_currentFunction = nullptr; } @@ -90,7 +89,7 @@ void SMTCheckerImpl::endVisit(VariableDeclarationStatement const& _varDecl) else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) // TODO more checks? // TODO add restrictions about type (might be assignment from smaller type) - m_solver.add(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + m_interface.addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); else m_errorReporter.warning( _varDecl.location(), @@ -120,7 +119,7 @@ void SMTCheckerImpl::endVisit(Assignment const& _assignment) if (knownVariable(*decl)) // TODO more checks? // TODO add restrictions about type (might be assignment from smaller type) - m_solver.add(newValue(*decl) == expr(_assignment.rightHandSide())); + m_interface.addAssertion(newValue(*decl) == expr(_assignment.rightHandSide())); else m_errorReporter.warning( _assignment.location(), @@ -142,7 +141,7 @@ void SMTCheckerImpl::endVisit(TupleExpression const& _tuple) "Assertion checker does not yet implement tules and inline arrays." ); else - m_solver.add(expr(_tuple) == expr(*_tuple.components()[0])); + m_interface.addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); } void SMTCheckerImpl::endVisit(BinaryOperation const& _op) @@ -170,13 +169,13 @@ void SMTCheckerImpl::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_solver.add(expr(*args[0])); + m_interface.addAssertion(expr(*args[0])); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); - m_solver.add(expr(*args[0])); + m_interface.addAssertion(expr(*args[0])); checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); // TODO is there something meaningful we can check here? // We can check whether the condition is always fulfilled or never fulfilled. @@ -189,7 +188,7 @@ void SMTCheckerImpl::endVisit(Identifier const& _identifier) solAssert(decl, ""); if (dynamic_cast(_identifier.annotation().type.get())) { - m_solver.add(expr(_identifier) == currentValue(*decl)); + m_interface.addAssertion(expr(_identifier) == currentValue(*decl)); return; } else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) @@ -214,7 +213,7 @@ void SMTCheckerImpl::endVisit(Literal const& _literal) if (RationalNumberType const* rational = dynamic_cast(&type)) solAssert(!rational->isFractional(), ""); - m_solver.add(expr(_literal) == m_context.int_val(type.literalValue(&_literal).str().c_str())); + m_interface.addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); } else m_errorReporter.warning( @@ -235,10 +234,10 @@ void SMTCheckerImpl::arithmeticOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); - z3::expr left(expr(_op.leftExpression())); - z3::expr right(expr(_op.rightExpression())); + smt::Expression left(expr(_op.leftExpression())); + smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); - z3::expr value( + smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : /*op == Token::Mul*/ left * right @@ -261,7 +260,7 @@ void SMTCheckerImpl::arithmeticOperation(BinaryOperation const& _op) &value ); - m_solver.add(expr(_op) == value); + m_interface.addAssertion(expr(_op) == value); break; } default: @@ -277,10 +276,10 @@ void SMTCheckerImpl::compareOperation(BinaryOperation const& _op) solAssert(_op.annotation().commonType, ""); if (_op.annotation().commonType->category() == Type::Category::Integer) { - z3::expr left(expr(_op.leftExpression())); - z3::expr right(expr(_op.rightExpression())); + smt::Expression left(expr(_op.leftExpression())); + smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); - z3::expr value = ( + smt::Expression value = ( op == Token::Equal ? (left == right) : op == Token::NotEqual ? (left != right) : op == Token::LessThan ? (left < right) : @@ -289,7 +288,7 @@ void SMTCheckerImpl::compareOperation(BinaryOperation const& _op) /*op == Token::GreaterThanOrEqual*/ (left >= right) ); // TODO: check that other values for op are not possible. - m_solver.add(expr(_op) == value); + m_interface.addAssertion(expr(_op) == value); } else m_errorReporter.warning( @@ -305,9 +304,9 @@ void SMTCheckerImpl::booleanOperation(BinaryOperation const& _op) if (_op.annotation().commonType->category() == Type::Category::Bool) { if (_op.getOperator() == Token::And) - m_solver.add(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); else - m_solver.add(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); } else m_errorReporter.warning( @@ -317,33 +316,32 @@ void SMTCheckerImpl::booleanOperation(BinaryOperation const& _op) } void SMTCheckerImpl::checkCondition( - z3::expr _condition, + smt::Expression _condition, SourceLocation const& _location, string const& _description, string const& _additionalValueName, - z3::expr* _additionalValue + smt::Expression* _additionalValue ) { - m_solver.push(); - m_solver.add(_condition); - switch (m_solver.check()) + m_interface.push(); + m_interface.addAssertion(_condition); + switch (m_interface.check()) { - case z3::check_result::sat: + case smt::CheckResult::SAT: { std::ostringstream message; message << _description << " happens here"; if (m_currentFunction) { message << " for:\n"; - z3::model m = m_solver.get_model(); if (_additionalValue) - message << " " << _additionalValueName << " = " << m.eval(*_additionalValue) << "\n"; + message << " " << _additionalValueName << " = " << m_interface.eval(*_additionalValue) << "\n"; for (auto const& param: m_currentFunction->parameters()) if (knownVariable(*param)) - message << " " << param->name() << " = " << m.eval(currentValue(*param)) << "\n"; + message << " " << param->name() << " = " << m_interface.eval(currentValue(*param)) << "\n"; for (auto const& var: m_currentFunction->localVariables()) if (knownVariable(*var)) - message << " " << var->name() << " = " << m.eval(currentValue(*var)) << "\n"; + message << " " << var->name() << " = " << m_interface.eval(currentValue(*var)) << "\n"; // message << m << endl; // message << m_solver << endl; } @@ -352,13 +350,13 @@ void SMTCheckerImpl::checkCondition( m_errorReporter.warning(_location, message.str()); break; } - case z3::check_result::unsat: + case smt::CheckResult::UNSAT: break; - case z3::check_result::unknown: + case smt::CheckResult::UNKNOWN: m_errorReporter.warning(_location, _description + " might happen here."); break; } - m_solver.pop(); + m_interface.pop(); } void SMTCheckerImpl::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) @@ -368,13 +366,13 @@ void SMTCheckerImpl::createVariable(VariableDeclaration const& _varDecl, bool _s solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_z3Variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; - m_z3Variables.emplace(&_varDecl, m_context.function(uniqueSymbol(_varDecl).c_str(), m_context.int_sort(), m_context.int_sort())); + m_z3Variables.emplace(&_varDecl, m_interface.newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); if (_setToZero) - m_solver.add(currentValue(_varDecl) == 0); + m_interface.addAssertion(currentValue(_varDecl) == 0); else { - m_solver.add(currentValue(_varDecl) >= minValue(*intType)); - m_solver.add(currentValue(_varDecl) <= maxValue(*intType)); + m_interface.addAssertion(currentValue(_varDecl) >= minValue(*intType)); + m_interface.addAssertion(currentValue(_varDecl) <= maxValue(*intType)); } } else @@ -399,30 +397,30 @@ bool SMTCheckerImpl::knownVariable(Declaration const& _decl) return m_currentSequenceCounter.count(&_decl); } -z3::expr SMTCheckerImpl::currentValue(Declaration const& _decl) +smt::Expression SMTCheckerImpl::currentValue(Declaration const& _decl) { solAssert(m_currentSequenceCounter.count(&_decl), ""); return var(_decl)(m_currentSequenceCounter.at(&_decl)); } -z3::expr SMTCheckerImpl::newValue(const Declaration& _decl) +smt::Expression SMTCheckerImpl::newValue(const Declaration& _decl) { solAssert(m_currentSequenceCounter.count(&_decl), ""); m_currentSequenceCounter[&_decl]++; return currentValue(_decl); } -z3::expr SMTCheckerImpl::minValue(IntegerType const& _t) +smt::Expression SMTCheckerImpl::minValue(IntegerType const& _t) { - return m_context.int_val(_t.minValue().str().c_str()); + return m_interface.newInteger(_t.minValue()); } -z3::expr SMTCheckerImpl::maxValue(IntegerType const& _t) +smt::Expression SMTCheckerImpl::maxValue(IntegerType const& _t) { - return m_context.int_val(_t.maxValue().str().c_str()); + return m_interface.newInteger(_t.maxValue()); } -z3::expr SMTCheckerImpl::expr(Expression const& _e) +smt::Expression SMTCheckerImpl::expr(Expression const& _e) { if (!m_z3Expressions.count(&_e)) { @@ -433,14 +431,14 @@ z3::expr SMTCheckerImpl::expr(Expression const& _e) { if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) solAssert(!rational->isFractional(), ""); - m_z3Expressions.emplace(&_e, m_context.int_const(uniqueSymbol(_e).c_str())); + m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); break; } case Type::Category::Integer: - m_z3Expressions.emplace(&_e, m_context.int_const(uniqueSymbol(_e).c_str())); + m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); break; case Type::Category::Bool: - m_z3Expressions.emplace(&_e, m_context.bool_const(uniqueSymbol(_e).c_str())); + m_z3Expressions.emplace(&_e, m_interface.newBool(uniqueSymbol(_e))); break; default: solAssert(false, "Type not implemented."); @@ -449,7 +447,7 @@ z3::expr SMTCheckerImpl::expr(Expression const& _e) return m_z3Expressions.at(&_e); } -z3::func_decl SMTCheckerImpl::var(Declaration const& _decl) +smt::Expression SMTCheckerImpl::var(Declaration const& _decl) { solAssert(m_z3Variables.count(&_decl), ""); return m_z3Variables.at(&_decl); diff --git a/libsolidity/formal/SMTCheckerImpl.h b/libsolidity/formal/SMTCheckerImpl.h index a82ec92ab5cd..e51794409c1e 100644 --- a/libsolidity/formal/SMTCheckerImpl.h +++ b/libsolidity/formal/SMTCheckerImpl.h @@ -18,8 +18,7 @@ #pragma once #include - -#include +#include #include #include @@ -60,11 +59,11 @@ class SMTCheckerImpl: private ASTConstVisitor void booleanOperation(BinaryOperation const& _op); void checkCondition( - z3::expr _condition, + smt::Expression _condition, SourceLocation const& _location, std::string const& _description, std::string const& _additionalValueName = "", - z3::expr* _additionalValue = nullptr + smt::Expression* _additionalValue = nullptr ); void createVariable(VariableDeclaration const& _varDecl, bool _setToZero); @@ -72,24 +71,23 @@ class SMTCheckerImpl: private ASTConstVisitor std::string uniqueSymbol(Declaration const& _decl); std::string uniqueSymbol(Expression const& _expr); bool knownVariable(Declaration const& _decl); - z3::expr currentValue(Declaration const& _decl); - z3::expr newValue(Declaration const& _decl); + smt::Expression currentValue(Declaration const& _decl); + smt::Expression newValue(Declaration const& _decl); - z3::expr minValue(IntegerType const& _t); - z3::expr maxValue(IntegerType const& _t); + smt::Expression minValue(IntegerType const& _t); + smt::Expression maxValue(IntegerType const& _t); /// Returns the z3 expression corresponding to the AST node. Creates a new expression /// if it does not exist yet. - z3::expr expr(Expression const& _e); + smt::Expression expr(Expression const& _e); /// Returns the z3 function declaration corresponding to the given variable. /// The function takes one argument which is the "sequence number". - z3::func_decl var(Declaration const& _decl); + smt::Expression var(Declaration const& _decl); - z3::context m_context; - z3::solver m_solver; + smt::SMTLib2Interface m_interface; std::map m_currentSequenceCounter; - std::map m_z3Expressions; - std::map m_z3Variables; + std::map m_z3Expressions; + std::map m_z3Variables; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp new file mode 100644 index 000000000000..c736ed2a3736 --- /dev/null +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -0,0 +1,24 @@ +/* + 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 + + +using namespace std; +using namespace dev; +using namespace dev::solidity::smt; + diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h new file mode 100644 index 000000000000..f984cfb508ad --- /dev/null +++ b/libsolidity/formal/SMTLib2Interface.h @@ -0,0 +1,150 @@ +/* + 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 smt +{ + +enum class CheckResult +{ + SAT, UNSAT, UNKNOWN +}; + +enum class Sort +{ + Int, Bool +}; + +class Expression +{ + friend class SMTLib2Interface; + /// Manual constructor, should only be used by SMTLib2Interface and the class itself. + Expression(std::string _name, std::vector _arguments): + m_name(std::move(_name)), m_arguments(std::move(_arguments)) {} + +public: + Expression(size_t _number): m_name(std::to_string(_number)) {} + Expression(u256 const& _number): m_name(std::to_string(_number)) {} + + Expression(Expression const& _other) = default; + Expression(Expression&& _other) = default; + Expression& operator=(Expression const& _other) = default; + Expression& operator=(Expression&& _other) = default; + + friend Expression operator!(Expression _a) + { + return Expression("not", _a); + } + friend Expression operator&&(Expression _a, Expression _b) + { + return Expression("and", _a, _b); + } + friend Expression operator||(Expression _a, Expression _b) + { + return Expression("or", _a, _b); + } + friend Expression operator==(Expression _a, Expression _b) + { + return Expression("=", _a, _b); + } + friend Expression operator!=(Expression _a, Expression _b) + { + return !(_a == _b); + } + friend Expression operator<(Expression _a, Expression _b) + { + return Expression("<", std::move(_a), std::move(_b)); + } + friend Expression operator<=(Expression _a, Expression _b) + { + return Expression("<=", std::move(_a), std::move(_b)); + } + friend Expression operator>(Expression _a, Expression _b) + { + return Expression(">", std::move(_a), std::move(_b)); + } + friend Expression operator>=(Expression _a, Expression _b) + { + return Expression(">=", std::move(_a), std::move(_b)); + } + friend Expression operator+(Expression _a, Expression _b) + { + return Expression("+", std::move(_a), std::move(_b)); + } + friend Expression operator-(Expression _a, Expression _b) + { + return Expression("-", std::move(_a), std::move(_b)); + } + friend Expression operator*(Expression _a, Expression _b) + { + return Expression("*", std::move(_a), std::move(_b)); + } + + std::string toSExpr() const + { + std::string sexpr = "(" + m_name; + for (auto const& arg: m_arguments) + sexpr += " " + arg.toSExpr(); + sexpr += ")"; + return sexpr; + } + +private: + explicit Expression(std::string _name): + Expression(std::move(_name), std::vector{}) {} + Expression(std::string _name, Expression _arg): + Expression(std::move(_name), std::vector{std::move(_arg)}) {} + Expression(std::string _name, Expression _arg1, Expression _arg2): + Expression(std::move(_name), std::vector{std::move(_arg1), std::move(_arg2)}) {} + + std::string const m_name; + std::vector const m_arguments; +}; + +class SMTLib2Interface +{ +public: + + void reset(); + + void push(); + void pop(); + + Expression newFunction(std::string _name, Sort _domain, Sort _codomain); + Expression newInteger(std::string _name); + Expression newBool(std::string _name); + + void addAssertion(Expression _expr); + CheckResult check(); + std::string eval(Expression _expr); +}; + + +} +} +} From 39fc798999b28386405399102d6cc7d199965d80 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 10 Jul 2017 21:21:11 +0200 Subject: [PATCH 131/163] Use file to communicate with z3. --- CMakeLists.txt | 1 - deps | 2 +- libsolidity/formal/SMTCheckerImpl.cpp | 31 +++- libsolidity/formal/SMTLib2Interface.cpp | 222 ++++++++++++++++++++++++ libsolidity/formal/SMTLib2Interface.h | 35 +++- 5 files changed, 275 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29ca63fe0108..da14ddb77135 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,6 @@ option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" # Let's find our dependencies include(EthDependencies) include(deps/jsoncpp.cmake) -include(deps/z3.cmake) find_package(Threads) diff --git a/deps b/deps index cd9c6ff69134..e5c8316db8d3 160000 --- a/deps +++ b/deps @@ -1 +1 @@ -Subproject commit cd9c6ff69134e8f53bde70d92cce12ad294752de +Subproject commit e5c8316db8d3daa0abc3b5af8545ce330057608c diff --git a/libsolidity/formal/SMTCheckerImpl.cpp b/libsolidity/formal/SMTCheckerImpl.cpp index 028e4400afa3..b86a0279ba53 100644 --- a/libsolidity/formal/SMTCheckerImpl.cpp +++ b/libsolidity/formal/SMTCheckerImpl.cpp @@ -325,25 +325,40 @@ void SMTCheckerImpl::checkCondition( { m_interface.push(); m_interface.addAssertion(_condition); - switch (m_interface.check()) + + vector expressionsToEvaluate; + if (m_currentFunction) + { + if (_additionalValue) + expressionsToEvaluate.emplace_back(*_additionalValue); + for (auto const& param: m_currentFunction->parameters()) + if (knownVariable(*param)) + expressionsToEvaluate.emplace_back(currentValue(*param)); + for (auto const& var: m_currentFunction->localVariables()) + if (knownVariable(*var)) + expressionsToEvaluate.emplace_back(currentValue(*var)); + } + smt::CheckResult result; + vector values; + tie(result, values) = m_interface.check(expressionsToEvaluate); + switch (result) { case smt::CheckResult::SAT: { std::ostringstream message; message << _description << " happens here"; + size_t i = 0; if (m_currentFunction) { message << " for:\n"; if (_additionalValue) - message << " " << _additionalValueName << " = " << m_interface.eval(*_additionalValue) << "\n"; + message << " " << _additionalValueName << " = " << values.at(i++) << "\n"; for (auto const& param: m_currentFunction->parameters()) if (knownVariable(*param)) - message << " " << param->name() << " = " << m_interface.eval(currentValue(*param)) << "\n"; + message << " " << param->name() << " = " << values.at(i++) << "\n"; for (auto const& var: m_currentFunction->localVariables()) if (knownVariable(*var)) - message << " " << var->name() << " = " << m_interface.eval(currentValue(*var)) << "\n"; -// message << m << endl; -// message << m_solver << endl; + message << " " << var->name() << " = " << values.at(i++) << "\n"; } else message << "."; @@ -412,12 +427,12 @@ smt::Expression SMTCheckerImpl::newValue(const Declaration& _decl) smt::Expression SMTCheckerImpl::minValue(IntegerType const& _t) { - return m_interface.newInteger(_t.minValue()); + return smt::Expression(_t.minValue()); } smt::Expression SMTCheckerImpl::maxValue(IntegerType const& _t) { - return m_interface.newInteger(_t.maxValue()); + return smt::Expression(_t.maxValue()); } smt::Expression SMTCheckerImpl::expr(Expression const& _e) diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index c736ed2a3736..8d40c3545d34 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -17,8 +17,230 @@ #include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include using namespace std; using namespace dev; using namespace dev::solidity::smt; + +//namespace +//{ + +//void createSubprocess(FILE*& _readPipe, FILE*& _writePipe) +//{ +// int pipe_in[2]; /* This is the pipe with wich we write to the child process. */ +// int pipe_out[2]; /* This is the pipe with wich we read from the child process. */ + +// solAssert(!pipe(pipe_in) && !pipe(pipe_out), ""); + +// /* Attempt to fork and check for errors */ +// pid_t pid = fork(); +// solAssert(pid != -1, ""); + +// if (pid) +// { +// /* The parent has the non-zero PID. */ +// _readPipe = fdopen(pipe_out[0], "r"); +// _writePipe = fdopen(pipe_in[1], "w"); +// close(pipe_out[1]); +// close(pipe_in[0]); +// } +// else +// { +// /* The child has the zero pid returned by fork*/ +// cout << "child" << endl; +// solAssert(dup2(pipe_out[1], 1) != -1, ""); +// solAssert(dup2(pipe_in[0], 0) != -1, ""); +// solAssert(close(pipe_out[0]) == 0, ""); +// solAssert(close(pipe_out[1]) == 0, ""); +// solAssert(close(pipe_in[0]) == 0, ""); +// solAssert(close(pipe_in[1]) == 0, ""); +// execl("/usr/bin/z3", "z3", "-smt2", "-in", NULL); +// exit(1); /* Only reached if execl() failed */ +// } +//} + +//} + +SMTLib2Interface::SMTLib2Interface() +{ + // TODO using pipes did not really work, so trying it the hard way for now. +// createSubprocess(m_solverWrite, m_solverRead); +// solAssert(m_solverWrite, "Unable to start Z3"); +// solAssert(m_solverRead, "Unable to start Z3"); +// write("(set-option :produce-models true)\n(set-logic QF_LIA)\n"); + reset(); +} + +SMTLib2Interface::~SMTLib2Interface() +{ +// if (m_solverWrite) +// { +// write("(exit)\n"); +// fflush(m_solverWrite); +// fclose(m_solverWrite); +// m_solverWrite = nullptr; +// } +// if (m_solverRead) +// { +// fclose(m_solverRead); +// m_solverRead = nullptr; +// } +} + +void SMTLib2Interface::reset() +{ +// write("(reset)\n"); +// write("(set-option :produce-models true)\n"); + m_accumulatedOutput.clear(); + m_accumulatedOutput.emplace_back(); + write("(set-option :produce-models true)\n(set-logic QF_UFLIA)\n"); +} + +void SMTLib2Interface::push() +{ + m_accumulatedOutput.emplace_back(); + //write("(push)\n"); +} + +void SMTLib2Interface::pop() +{ + //write("(pop)\n"); + solAssert(!m_accumulatedOutput.empty(), ""); + m_accumulatedOutput.pop_back(); +} + +Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codomain) +{ + write( + "(declare-fun |" + + _name + + "| (" + + (_domain == Sort::Int ? "Int" : "Bool") + + ") " + + (_codomain == Sort::Int ? "Int" : "Bool") + + ")\n" + ); + return Expression(_name, {}); +} + +Expression SMTLib2Interface::newInteger(string _name) +{ + write("(declare-const |" + _name + "| Int)\n"); + return Expression(_name, {}); +} + +Expression SMTLib2Interface::newBool(string _name) +{ + write("(declare-const |" + _name + "| Bool)\n"); + return Expression(_name, {}); +} + +void SMTLib2Interface::addAssertion(Expression _expr) +{ + write("(assert " + _expr.toSExpr() + ")\n"); +} + +pair> SMTLib2Interface::check(vector const& _expressionsToEvaluate) +{ + string checks; + if (_expressionsToEvaluate.empty()) + checks = "(check-sat)\n"; + else + { + // TODO make sure these are unique + for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) + { + auto const& e = _expressionsToEvaluate.at(i); + // TODO they don't have to be ints... + checks += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; + checks += "(assert (= |EVALEXPR_" + to_string(i) + "| " + e.toSExpr() + "))\n"; + } + checks += "(check-sat)\n"; + checks += "(get-value ("; + for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) + checks += "|EVALEXPR_" + to_string(i) + "| "; + checks += "))\n"; + } + string response = communicate(boost::algorithm::join(m_accumulatedOutput, "\n") + checks); + CheckResult result; + // TODO proper parsing + if (boost::starts_with(response, "sat\n")) + result = CheckResult::SAT; + else if (boost::starts_with(response, "unsat\n")) + result = CheckResult::UNSAT; + else if (boost::starts_with(response, "unknown\n")) + result = CheckResult::UNKNOWN; + else + solAssert(false, "Invalid response to check-sat: " + response); + + vector values; + if (result != CheckResult::UNSAT) + values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend()); + return make_pair(result, values); +} + +//string SMTLib2Interface::eval(Expression _expr) +//{ +// write("(get-value (" + _expr.toSExpr() + ")\n"); +// std::string reply = communicate(); +// cout << "<-- Z3: " << reply << endl; +// // TODO parse +// return reply; +//} + +void SMTLib2Interface::write(string _data) +{ +// cout << " --> Z3: " << _data << endl; +// solAssert(m_solverWrite, ""); +// solAssert(fputs(_data.c_str(), m_solverWrite) >= 0 || true, "EOF while communicating with Z3."); +// solAssert(fflush(m_solverWrite) == 0 || true, ""); + solAssert(!m_accumulatedOutput.empty(), ""); + m_accumulatedOutput.back() += move(_data); +} + +string SMTLib2Interface::communicate(std::string const& _input) +{ + ofstream("/tmp/z3exchange.smt2") << _input << "(exit)" << endl; + FILE* solverOutput = popen("z3 -smt2 /tmp/z3exchange.smt2", "r"); + string result; + array buffer; + while (!feof(solverOutput)) + if (fgets(buffer.data(), 127, solverOutput) != nullptr) + result += buffer.data(); + fclose(solverOutput); + return result; +} + +vector SMTLib2Interface::parseValues(string::const_iterator _start, string::const_iterator _end) +{ + vector values; + while (_start < _end) + { + auto valStart = find(_start, _end, ' '); + if (valStart < _end) + ++valStart; + auto valEnd = find(valStart, _end, ')'); + values.emplace_back(valStart, valEnd); + _start = find(valEnd, _end, '('); + } + + return values; +} diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index f984cfb508ad..b7bab0437aab 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -17,11 +17,14 @@ #pragma once +#include + +#include + #include #include #include - -#include +#include namespace dev { @@ -49,7 +52,8 @@ class Expression public: Expression(size_t _number): m_name(std::to_string(_number)) {} - Expression(u256 const& _number): m_name(std::to_string(_number)) {} + Expression(u256 const& _number): m_name(_number.str()) {} + Expression(bigint const& _number): m_name(_number.str()) {} Expression(Expression const& _other) = default; Expression(Expression&& _other) = default; @@ -104,9 +108,16 @@ class Expression { return Expression("*", std::move(_a), std::move(_b)); } + Expression operator()(Expression _a) const + { + solAssert(m_arguments.empty(), "Attempted function application to non-function."); + return Expression(m_name, _a); + } std::string toSExpr() const { + if (m_arguments.empty()) + return m_name; std::string sexpr = "(" + m_name; for (auto const& arg: m_arguments) sexpr += " " + arg.toSExpr(); @@ -126,9 +137,11 @@ class Expression std::vector const m_arguments; }; -class SMTLib2Interface +class SMTLib2Interface: public boost::noncopyable { public: + SMTLib2Interface(); + ~SMTLib2Interface(); void reset(); @@ -140,8 +153,18 @@ class SMTLib2Interface Expression newBool(std::string _name); void addAssertion(Expression _expr); - CheckResult check(); - std::string eval(Expression _expr); + std::pair> check(std::vector const& _expressionsToEvaluate); +// std::string eval(Expression _expr); + +private: + void write(std::string _data); + std::string communicate(std::string const& _input); + + std::vector parseValues(std::string::const_iterator _start, std::string::const_iterator _end); + + std::vector m_accumulatedOutput; +// FILE* m_solverWrite = nullptr; +// FILE* m_solverRead = nullptr; }; From b3f8ed457a10dab36abaef72310a755a95e0753f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 11 Jul 2017 13:26:43 +0200 Subject: [PATCH 132/163] Cleanup. --- cmake/UseSolidity.cmake | 2 +- libsolidity/formal/SMTChecker.cpp | 447 +++++++++++++++++- libsolidity/formal/SMTChecker.h | 65 ++- libsolidity/formal/SMTCheckerImpl.cpp | 469 ------------------- libsolidity/formal/SMTCheckerImpl.h | 97 ---- libsolidity/formal/SMTLib2Interface.cpp | 168 ++----- libsolidity/formal/SMTLib2Interface.h | 29 +- libsolidity/formal/SMTSolverCommunicator.cpp | 75 +++ libsolidity/formal/SMTSolverCommunicator.h | 50 ++ libsolidity/interface/CompilerStack.cpp | 2 +- 10 files changed, 687 insertions(+), 717 deletions(-) delete mode 100644 libsolidity/formal/SMTCheckerImpl.cpp delete mode 100644 libsolidity/formal/SMTCheckerImpl.h create mode 100644 libsolidity/formal/SMTSolverCommunicator.cpp create mode 100644 libsolidity/formal/SMTSolverCommunicator.h diff --git a/cmake/UseSolidity.cmake b/cmake/UseSolidity.cmake index 9e2dea3ea62c..5080f71b4ae4 100644 --- a/cmake/UseSolidity.cmake +++ b/cmake/UseSolidity.cmake @@ -25,7 +25,7 @@ function(eth_apply TARGET REQUIRED SUBMODULE) if (${SUBMODULE} STREQUAL "solidity" OR ${SUBMODULE} STREQUAL "") eth_use(${TARGET} ${REQUIRED} Dev::soldevcore Solidity::solevmasm) - target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES} z3) + target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES}) endif() target_compile_definitions(${TARGET} PUBLIC ETH_SOLIDITY) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 0e559fae5b79..b9e0e8f362bc 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -17,7 +17,6 @@ #include -#include #include @@ -25,12 +24,452 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SMTChecker::SMTChecker(ErrorReporter& _errorReporter): - m_impl(make_shared(_errorReporter)) +SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadFile::Callback const& _readFileCallback): + m_interface(_readFileCallback), + m_errorReporter(_errorReporter) { } void SMTChecker::analyze(SourceUnit const& _source) { - m_impl->analyze(_source); + bool pragmaFound = false; + for (auto const& node: _source.nodes()) + if (auto const* pragma = dynamic_cast(node.get())) + if (pragma->literals()[0] == "checkAssertionsZ3") + pragmaFound = true; + if (pragmaFound) + { + m_interface.reset(); + m_currentSequenceCounter.clear(); + _source.accept(*this); + } +} + +void SMTChecker::endVisit(VariableDeclaration const& _varDecl) +{ + if (_varDecl.value()) + { + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support this." + ); + } + else if (_varDecl.isLocalOrReturn()) + createVariable(_varDecl, true); + else if (_varDecl.isCallableParameter()) + createVariable(_varDecl, false); +} + +bool SMTChecker::visit(FunctionDefinition const& _function) +{ + if (!_function.modifiers().empty() || _function.isConstructor()) + m_errorReporter.warning( + _function.location(), + "Assertion checker does not yet support constructors and functions with modifiers." + ); + // TODO actually we probably also have to reset all local variables and similar things. + m_currentFunction = &_function; + m_interface.push(); + return true; +} + +void SMTChecker::endVisit(FunctionDefinition const&) +{ + // TOOD we could check for "reachability", i.e. satisfiability here. + m_interface.pop(); + m_currentFunction = nullptr; +} + +void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) +{ + if (_varDecl.declarations().size() != 1) + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support such variable declarations." + ); + else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) + // TODO more checks? + // TODO add restrictions about type (might be assignment from smaller type) + m_interface.addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + else + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet implement such variable declarations." + ); +} + +void SMTChecker::endVisit(ExpressionStatement const&) +{ +} + +void SMTChecker::endVisit(Assignment const& _assignment) +{ + if (_assignment.assignmentOperator() != Token::Value::Assign) + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement compound assignment." + ); + else if (_assignment.annotation().type->category() != Type::Category::Integer) + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() + ); + else if (Identifier const* identifier = dynamic_cast(&_assignment.leftHandSide())) + { + Declaration const* decl = identifier->annotation().referencedDeclaration; + if (knownVariable(*decl)) + // TODO more checks? + // TODO add restrictions about type (might be assignment from smaller type) + m_interface.addAssertion(newValue(*decl) == expr(_assignment.rightHandSide())); + else + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement such assignments." + ); + } + else + m_errorReporter.warning( + _assignment.location(), + "Assertion checker does not yet implement such assignments." + ); +} + +void SMTChecker::endVisit(TupleExpression const& _tuple) +{ + if (_tuple.isInlineArray() || _tuple.components().size() != 1) + m_errorReporter.warning( + _tuple.location(), + "Assertion checker does not yet implement tules and inline arrays." + ); + else + m_interface.addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); +} + +void SMTChecker::endVisit(BinaryOperation const& _op) +{ + if (Token::isArithmeticOp(_op.getOperator())) + arithmeticOperation(_op); + else if (Token::isCompareOp(_op.getOperator())) + compareOperation(_op); + else if (Token::isBooleanOp(_op.getOperator())) + booleanOperation(_op); + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); +} + +void SMTChecker::endVisit(FunctionCall const& _funCall) +{ + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + + std::vector> const args = _funCall.arguments(); + if (funType.kind() == FunctionType::Kind::Assert) + { + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); + m_interface.addAssertion(expr(*args[0])); + } + else if (funType.kind() == FunctionType::Kind::Require) + { + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + m_interface.addAssertion(expr(*args[0])); + checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); + // TODO is there something meaningful we can check here? + // We can check whether the condition is always fulfilled or never fulfilled. + } +} + +void SMTChecker::endVisit(Identifier const& _identifier) +{ + Declaration const* decl = _identifier.annotation().referencedDeclaration; + solAssert(decl, ""); + if (dynamic_cast(_identifier.annotation().type.get())) + { + m_interface.addAssertion(expr(_identifier) == currentValue(*decl)); + return; + } + else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) + { + if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) + return; + // TODO for others, clear our knowledge about storage and memory + } + m_errorReporter.warning( + _identifier.location(), + "Assertion checker does not yet support the type of this expression (" + + _identifier.annotation().type->toString() + + ")." + ); +} + +void SMTChecker::endVisit(Literal const& _literal) +{ + Type const& type = *_literal.annotation().type; + if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber) + { + if (RationalNumberType const* rational = dynamic_cast(&type)) + solAssert(!rational->isFractional(), ""); + + m_interface.addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); + } + else + m_errorReporter.warning( + _literal.location(), + "Assertion checker does not yet support the type of this expression (" + + _literal.annotation().type->toString() + + ")." + ); +} + +void SMTChecker::arithmeticOperation(BinaryOperation const& _op) +{ + switch (_op.getOperator()) + { + case Token::Add: + case Token::Sub: + case Token::Mul: + { + solAssert(_op.annotation().commonType, ""); + solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); + smt::Expression left(expr(_op.leftExpression())); + smt::Expression right(expr(_op.rightExpression())); + Token::Value op = _op.getOperator(); + smt::Expression value( + op == Token::Add ? left + right : + op == Token::Sub ? left - right : + /*op == Token::Mul*/ left * right + ); + + // Overflow check + auto const& intType = dynamic_cast(*_op.annotation().commonType); + checkCondition( + value < minValue(intType), + _op.location(), + "Underflow (resulting value less than " + intType.minValue().str() + ")", + "value", + &value + ); + checkCondition( + value > maxValue(intType), + _op.location(), + "Overflow (resulting value larger than " + intType.maxValue().str() + ")", + "value", + &value + ); + + m_interface.addAssertion(expr(_op) == value); + break; + } + default: + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); + } +} + +void SMTChecker::compareOperation(BinaryOperation const& _op) +{ + solAssert(_op.annotation().commonType, ""); + if (_op.annotation().commonType->category() == Type::Category::Integer) + { + 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) + ); + // TODO: check that other values for op are not possible. + m_interface.addAssertion(expr(_op) == value); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons" + ); +} + +void SMTChecker::booleanOperation(BinaryOperation const& _op) +{ + solAssert(_op.getOperator() == Token::And || _op.getOperator() == Token::Or, ""); + solAssert(_op.annotation().commonType, ""); + if (_op.annotation().commonType->category() == Type::Category::Bool) + { + if (_op.getOperator() == Token::And) + m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + else + m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations" + ); +} + +void SMTChecker::checkCondition( + smt::Expression _condition, + SourceLocation const& _location, + string const& _description, + string const& _additionalValueName, + smt::Expression* _additionalValue +) +{ + m_interface.push(); + m_interface.addAssertion(_condition); + + vector expressionsToEvaluate; + if (m_currentFunction) + { + if (_additionalValue) + expressionsToEvaluate.emplace_back(*_additionalValue); + for (auto const& param: m_currentFunction->parameters()) + if (knownVariable(*param)) + expressionsToEvaluate.emplace_back(currentValue(*param)); + for (auto const& var: m_currentFunction->localVariables()) + if (knownVariable(*var)) + expressionsToEvaluate.emplace_back(currentValue(*var)); + } + smt::CheckResult result; + vector values; + tie(result, values) = m_interface.check(expressionsToEvaluate); + switch (result) + { + case smt::CheckResult::SAT: + { + std::ostringstream message; + message << _description << " happens here"; + size_t i = 0; + if (m_currentFunction) + { + message << " for:\n"; + if (_additionalValue) + message << " " << _additionalValueName << " = " << values.at(i++) << "\n"; + for (auto const& param: m_currentFunction->parameters()) + if (knownVariable(*param)) + message << " " << param->name() << " = " << values.at(i++) << "\n"; + for (auto const& var: m_currentFunction->localVariables()) + if (knownVariable(*var)) + message << " " << var->name() << " = " << values.at(i++) << "\n"; + } + else + message << "."; + m_errorReporter.warning(_location, message.str()); + break; + } + case smt::CheckResult::UNSAT: + break; + case smt::CheckResult::UNKNOWN: + m_errorReporter.warning(_location, _description + " might happen here."); + break; + case smt::CheckResult::ERROR: + m_errorReporter.warning(_location, "Error trying to invoke SMT solver."); + break; + default: + solAssert(false, ""); + } + m_interface.pop(); +} + +void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) +{ + if (auto intType = dynamic_cast(_varDecl.type().get())) + { + solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); + solAssert(m_z3Variables.count(&_varDecl) == 0, ""); + m_currentSequenceCounter[&_varDecl] = 0; + m_z3Variables.emplace(&_varDecl, m_interface.newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + if (_setToZero) + m_interface.addAssertion(currentValue(_varDecl) == 0); + else + { + m_interface.addAssertion(currentValue(_varDecl) >= minValue(*intType)); + m_interface.addAssertion(currentValue(_varDecl) <= maxValue(*intType)); + } + } + else + m_errorReporter.warning( + _varDecl.location(), + "Assertion checker does not yet support the type of this variable." + ); +} + +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()); +} + +bool SMTChecker::knownVariable(Declaration const& _decl) +{ + return m_currentSequenceCounter.count(&_decl); +} + +smt::Expression SMTChecker::currentValue(Declaration const& _decl) +{ + solAssert(m_currentSequenceCounter.count(&_decl), ""); + return var(_decl)(m_currentSequenceCounter.at(&_decl)); +} + +smt::Expression SMTChecker::newValue(const Declaration& _decl) +{ + solAssert(m_currentSequenceCounter.count(&_decl), ""); + m_currentSequenceCounter[&_decl]++; + return currentValue(_decl); +} + +smt::Expression SMTChecker::minValue(IntegerType const& _t) +{ + return smt::Expression(_t.minValue()); +} + +smt::Expression SMTChecker::maxValue(IntegerType const& _t) +{ + return smt::Expression(_t.maxValue()); +} + +smt::Expression SMTChecker::expr(Expression const& _e) +{ + if (!m_z3Expressions.count(&_e)) + { + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) + { + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); + break; + } + case Type::Category::Integer: + m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); + break; + case Type::Category::Bool: + m_z3Expressions.emplace(&_e, m_interface.newBool(uniqueSymbol(_e))); + break; + default: + solAssert(false, "Type not implemented."); + } + } + return m_z3Expressions.at(&_e); +} + +smt::Expression SMTChecker::var(Declaration const& _decl) +{ + solAssert(m_z3Variables.count(&_decl), ""); + return m_z3Variables.at(&_decl); } diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 7e7ee3d90cda..8c6a23270f7b 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -17,9 +17,12 @@ #pragma once +#include +#include +#include + #include #include -#include namespace dev { @@ -27,18 +30,68 @@ namespace solidity { class ErrorReporter; -class SourceUnit; -class SMTCheckerImpl; -class SMTChecker +class SMTChecker: private ASTConstVisitor { public: - SMTChecker(ErrorReporter& _errorReporter); + SMTChecker(ErrorReporter& _errorReporter, ReadFile::Callback const& _readCallback); void analyze(SourceUnit const& _sources); private: - std::shared_ptr m_impl; + // TODO: Check that we do not have concurrent reads and writes to a variable, + // because the order of expression evaluation is undefined + // TODO: or just force a certain order, but people might have a different idea about that. + + virtual void endVisit(VariableDeclaration const& _node) override; + virtual bool visit(FunctionDefinition const& _node) override; + virtual void endVisit(FunctionDefinition const& _node) override; + virtual void endVisit(VariableDeclarationStatement const& _node) override; + virtual void endVisit(ExpressionStatement const& _node) override; + virtual void endVisit(Assignment const& _node) override; + virtual void endVisit(TupleExpression const& _node) override; + virtual void endVisit(BinaryOperation const& _node) override; + virtual void endVisit(FunctionCall const& _node) override; + virtual void endVisit(Identifier const& _node) override; + virtual void endVisit(Literal const& _node) override; + + void arithmeticOperation(BinaryOperation const& _op); + void compareOperation(BinaryOperation const& _op); + void booleanOperation(BinaryOperation const& _op); + + void checkCondition( + smt::Expression _condition, + SourceLocation const& _location, + std::string const& _description, + std::string const& _additionalValueName = "", + smt::Expression* _additionalValue = nullptr + ); + + void createVariable(VariableDeclaration const& _varDecl, bool _setToZero); + + std::string uniqueSymbol(Declaration const& _decl); + std::string uniqueSymbol(Expression const& _expr); + bool knownVariable(Declaration const& _decl); + smt::Expression currentValue(Declaration const& _decl); + smt::Expression newValue(Declaration const& _decl); + + smt::Expression minValue(IntegerType const& _t); + smt::Expression maxValue(IntegerType const& _t); + + /// Returns the expression corresponding to the AST node. Creates a new expression + /// if it does not exist yet. + smt::Expression expr(Expression const& _e); + /// 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); + + smt::SMTLib2Interface m_interface; + std::map m_currentSequenceCounter; + std::map m_z3Expressions; + std::map m_z3Variables; + ErrorReporter& m_errorReporter; + + FunctionDefinition const* m_currentFunction = nullptr; }; } diff --git a/libsolidity/formal/SMTCheckerImpl.cpp b/libsolidity/formal/SMTCheckerImpl.cpp deleted file mode 100644 index b86a0279ba53..000000000000 --- a/libsolidity/formal/SMTCheckerImpl.cpp +++ /dev/null @@ -1,469 +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 - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SMTCheckerImpl::SMTCheckerImpl(ErrorReporter& _errorReporter): - m_errorReporter(_errorReporter) -{ -} - -void SMTCheckerImpl::analyze(SourceUnit const& _source) -{ - bool pragmaFound = false; - for (auto const& node: _source.nodes()) - if (auto const* pragma = dynamic_cast(node.get())) - if (pragma->literals()[0] == "checkAssertionsZ3") - pragmaFound = true; - if (pragmaFound) - { - m_interface.reset(); - m_currentSequenceCounter.clear(); - _source.accept(*this); - } -} - -void SMTCheckerImpl::endVisit(VariableDeclaration const& _varDecl) -{ - if (_varDecl.value()) - { - m_errorReporter.warning( - _varDecl.location(), - "Assertion checker does not yet support this." - ); - } - else if (_varDecl.isLocalOrReturn()) - createVariable(_varDecl, true); - else if (_varDecl.isCallableParameter()) - createVariable(_varDecl, false); -} - -bool SMTCheckerImpl::visit(FunctionDefinition const& _function) -{ - if (!_function.modifiers().empty() || _function.isConstructor()) - m_errorReporter.warning( - _function.location(), - "Assertion checker does not yet support constructors and functions with modifiers." - ); - // TODO actually we probably also have to reset all local variables and similar things. - m_currentFunction = &_function; - m_interface.push(); - return true; -} - -void SMTCheckerImpl::endVisit(FunctionDefinition const&) -{ - // TOOD we could check for "reachability", i.e. satisfiability here. - m_interface.pop(); - m_currentFunction = nullptr; -} - -void SMTCheckerImpl::endVisit(VariableDeclarationStatement const& _varDecl) -{ - if (_varDecl.declarations().size() != 1) - m_errorReporter.warning( - _varDecl.location(), - "Assertion checker does not yet support such variable declarations." - ); - else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) - // TODO more checks? - // TODO add restrictions about type (might be assignment from smaller type) - m_interface.addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); - else - m_errorReporter.warning( - _varDecl.location(), - "Assertion checker does not yet implement such variable declarations." - ); -} - -void SMTCheckerImpl::endVisit(ExpressionStatement const&) -{ -} - -void SMTCheckerImpl::endVisit(Assignment const& _assignment) -{ - if (_assignment.assignmentOperator() != Token::Value::Assign) - m_errorReporter.warning( - _assignment.location(), - "Assertion checker does not yet implement compound assignment." - ); - else if (_assignment.annotation().type->category() != Type::Category::Integer) - m_errorReporter.warning( - _assignment.location(), - "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() - ); - else if (Identifier const* identifier = dynamic_cast(&_assignment.leftHandSide())) - { - Declaration const* decl = identifier->annotation().referencedDeclaration; - if (knownVariable(*decl)) - // TODO more checks? - // TODO add restrictions about type (might be assignment from smaller type) - m_interface.addAssertion(newValue(*decl) == expr(_assignment.rightHandSide())); - else - m_errorReporter.warning( - _assignment.location(), - "Assertion checker does not yet implement such assignments." - ); - } - else - m_errorReporter.warning( - _assignment.location(), - "Assertion checker does not yet implement such assignments." - ); -} - -void SMTCheckerImpl::endVisit(TupleExpression const& _tuple) -{ - if (_tuple.isInlineArray() || _tuple.components().size() != 1) - m_errorReporter.warning( - _tuple.location(), - "Assertion checker does not yet implement tules and inline arrays." - ); - else - m_interface.addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); -} - -void SMTCheckerImpl::endVisit(BinaryOperation const& _op) -{ - if (Token::isArithmeticOp(_op.getOperator())) - arithmeticOperation(_op); - else if (Token::isCompareOp(_op.getOperator())) - compareOperation(_op); - else if (Token::isBooleanOp(_op.getOperator())) - booleanOperation(_op); - else - m_errorReporter.warning( - _op.location(), - "Assertion checker does not yet implement this operator." - ); -} - -void SMTCheckerImpl::endVisit(FunctionCall const& _funCall) -{ - FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); - - std::vector> const args = _funCall.arguments(); - if (funType.kind() == FunctionType::Kind::Assert) - { - solAssert(args.size() == 1, ""); - solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); - checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface.addAssertion(expr(*args[0])); - } - else if (funType.kind() == FunctionType::Kind::Require) - { - solAssert(args.size() == 1, ""); - solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); - m_interface.addAssertion(expr(*args[0])); - checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); - // TODO is there something meaningful we can check here? - // We can check whether the condition is always fulfilled or never fulfilled. - } -} - -void SMTCheckerImpl::endVisit(Identifier const& _identifier) -{ - Declaration const* decl = _identifier.annotation().referencedDeclaration; - solAssert(decl, ""); - if (dynamic_cast(_identifier.annotation().type.get())) - { - m_interface.addAssertion(expr(_identifier) == currentValue(*decl)); - return; - } - else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) - { - if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) - return; - // TODO for others, clear our knowledge about storage and memory - } - m_errorReporter.warning( - _identifier.location(), - "Assertion checker does not yet support the type of this expression (" + - _identifier.annotation().type->toString() + - ")." - ); -} - -void SMTCheckerImpl::endVisit(Literal const& _literal) -{ - Type const& type = *_literal.annotation().type; - if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber) - { - if (RationalNumberType const* rational = dynamic_cast(&type)) - solAssert(!rational->isFractional(), ""); - - m_interface.addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); - } - else - m_errorReporter.warning( - _literal.location(), - "Assertion checker does not yet support the type of this expression (" + - _literal.annotation().type->toString() + - ")." - ); -} - -void SMTCheckerImpl::arithmeticOperation(BinaryOperation const& _op) -{ - switch (_op.getOperator()) - { - case Token::Add: - case Token::Sub: - case Token::Mul: - { - solAssert(_op.annotation().commonType, ""); - solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); - smt::Expression left(expr(_op.leftExpression())); - smt::Expression right(expr(_op.rightExpression())); - Token::Value op = _op.getOperator(); - smt::Expression value( - op == Token::Add ? left + right : - op == Token::Sub ? left - right : - /*op == Token::Mul*/ left * right - ); - - // Overflow check - auto const& intType = dynamic_cast(*_op.annotation().commonType); - checkCondition( - value < minValue(intType), - _op.location(), - "Underflow (resulting value less than " + intType.minValue().str() + ")", - "value", - &value - ); - checkCondition( - value > maxValue(intType), - _op.location(), - "Overflow (resulting value larger than " + intType.maxValue().str() + ")", - "value", - &value - ); - - m_interface.addAssertion(expr(_op) == value); - break; - } - default: - m_errorReporter.warning( - _op.location(), - "Assertion checker does not yet implement this operator." - ); - } -} - -void SMTCheckerImpl::compareOperation(BinaryOperation const& _op) -{ - solAssert(_op.annotation().commonType, ""); - if (_op.annotation().commonType->category() == Type::Category::Integer) - { - 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) - ); - // TODO: check that other values for op are not possible. - m_interface.addAssertion(expr(_op) == value); - } - else - m_errorReporter.warning( - _op.location(), - "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons" - ); -} - -void SMTCheckerImpl::booleanOperation(BinaryOperation const& _op) -{ - solAssert(_op.getOperator() == Token::And || _op.getOperator() == Token::Or, ""); - solAssert(_op.annotation().commonType, ""); - if (_op.annotation().commonType->category() == Type::Category::Bool) - { - if (_op.getOperator() == Token::And) - m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); - else - m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); - } - else - m_errorReporter.warning( - _op.location(), - "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations" - ); -} - -void SMTCheckerImpl::checkCondition( - smt::Expression _condition, - SourceLocation const& _location, - string const& _description, - string const& _additionalValueName, - smt::Expression* _additionalValue -) -{ - m_interface.push(); - m_interface.addAssertion(_condition); - - vector expressionsToEvaluate; - if (m_currentFunction) - { - if (_additionalValue) - expressionsToEvaluate.emplace_back(*_additionalValue); - for (auto const& param: m_currentFunction->parameters()) - if (knownVariable(*param)) - expressionsToEvaluate.emplace_back(currentValue(*param)); - for (auto const& var: m_currentFunction->localVariables()) - if (knownVariable(*var)) - expressionsToEvaluate.emplace_back(currentValue(*var)); - } - smt::CheckResult result; - vector values; - tie(result, values) = m_interface.check(expressionsToEvaluate); - switch (result) - { - case smt::CheckResult::SAT: - { - std::ostringstream message; - message << _description << " happens here"; - size_t i = 0; - if (m_currentFunction) - { - message << " for:\n"; - if (_additionalValue) - message << " " << _additionalValueName << " = " << values.at(i++) << "\n"; - for (auto const& param: m_currentFunction->parameters()) - if (knownVariable(*param)) - message << " " << param->name() << " = " << values.at(i++) << "\n"; - for (auto const& var: m_currentFunction->localVariables()) - if (knownVariable(*var)) - message << " " << var->name() << " = " << values.at(i++) << "\n"; - } - else - message << "."; - m_errorReporter.warning(_location, message.str()); - break; - } - case smt::CheckResult::UNSAT: - break; - case smt::CheckResult::UNKNOWN: - m_errorReporter.warning(_location, _description + " might happen here."); - break; - } - m_interface.pop(); -} - -void SMTCheckerImpl::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) -{ - if (auto intType = dynamic_cast(_varDecl.type().get())) - { - solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_z3Variables.count(&_varDecl) == 0, ""); - m_currentSequenceCounter[&_varDecl] = 0; - m_z3Variables.emplace(&_varDecl, m_interface.newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); - if (_setToZero) - m_interface.addAssertion(currentValue(_varDecl) == 0); - else - { - m_interface.addAssertion(currentValue(_varDecl) >= minValue(*intType)); - m_interface.addAssertion(currentValue(_varDecl) <= maxValue(*intType)); - } - } - else - m_errorReporter.warning( - _varDecl.location(), - "Assertion checker does not yet support the type of this variable." - ); -} - -string SMTCheckerImpl::uniqueSymbol(Declaration const& _decl) -{ - return _decl.name() + "_" + to_string(_decl.id()); -} - -string SMTCheckerImpl::uniqueSymbol(Expression const& _expr) -{ - return "expr_" + to_string(_expr.id()); -} - -bool SMTCheckerImpl::knownVariable(Declaration const& _decl) -{ - return m_currentSequenceCounter.count(&_decl); -} - -smt::Expression SMTCheckerImpl::currentValue(Declaration const& _decl) -{ - solAssert(m_currentSequenceCounter.count(&_decl), ""); - return var(_decl)(m_currentSequenceCounter.at(&_decl)); -} - -smt::Expression SMTCheckerImpl::newValue(const Declaration& _decl) -{ - solAssert(m_currentSequenceCounter.count(&_decl), ""); - m_currentSequenceCounter[&_decl]++; - return currentValue(_decl); -} - -smt::Expression SMTCheckerImpl::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SMTCheckerImpl::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); -} - -smt::Expression SMTCheckerImpl::expr(Expression const& _e) -{ - if (!m_z3Expressions.count(&_e)) - { - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) - { - case Type::Category::RationalNumber: - { - if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_z3Expressions.emplace(&_e, m_interface.newBool(uniqueSymbol(_e))); - break; - default: - solAssert(false, "Type not implemented."); - } - } - return m_z3Expressions.at(&_e); -} - -smt::Expression SMTCheckerImpl::var(Declaration const& _decl) -{ - solAssert(m_z3Variables.count(&_decl), ""); - return m_z3Variables.at(&_decl); -} diff --git a/libsolidity/formal/SMTCheckerImpl.h b/libsolidity/formal/SMTCheckerImpl.h deleted file mode 100644 index e51794409c1e..000000000000 --- a/libsolidity/formal/SMTCheckerImpl.h +++ /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 . -*/ - -#pragma once - -#include -#include - -#include -#include - -namespace dev -{ -namespace solidity -{ - -class ErrorReporter; - -class SMTCheckerImpl: private ASTConstVisitor -{ -public: - SMTCheckerImpl(ErrorReporter& _errorReporter); - - void analyze(SourceUnit const& _sources); - -private: - // TODO: Check that we do not have concurrent reads and writes to a variable, - // because the order of expression evaluation is undefined - // TODO: or just force a certain order, but people might have a different idea about that. - - virtual void endVisit(VariableDeclaration const& _node) override; - virtual bool visit(FunctionDefinition const& _node) override; - virtual void endVisit(FunctionDefinition const& _node) override; - virtual void endVisit(VariableDeclarationStatement const& _node) override; - virtual void endVisit(ExpressionStatement const& _node) override; - virtual void endVisit(Assignment const& _node) override; - virtual void endVisit(TupleExpression const& _node) override; - virtual void endVisit(BinaryOperation const& _node) override; - virtual void endVisit(FunctionCall const& _node) override; - virtual void endVisit(Identifier const& _node) override; - virtual void endVisit(Literal const& _node) override; - - void arithmeticOperation(BinaryOperation const& _op); - void compareOperation(BinaryOperation const& _op); - void booleanOperation(BinaryOperation const& _op); - - void checkCondition( - smt::Expression _condition, - SourceLocation const& _location, - std::string const& _description, - std::string const& _additionalValueName = "", - smt::Expression* _additionalValue = nullptr - ); - - void createVariable(VariableDeclaration const& _varDecl, bool _setToZero); - - std::string uniqueSymbol(Declaration const& _decl); - std::string uniqueSymbol(Expression const& _expr); - bool knownVariable(Declaration const& _decl); - smt::Expression currentValue(Declaration const& _decl); - smt::Expression newValue(Declaration const& _decl); - - smt::Expression minValue(IntegerType const& _t); - smt::Expression maxValue(IntegerType const& _t); - - /// Returns the z3 expression corresponding to the AST node. Creates a new expression - /// if it does not exist yet. - smt::Expression expr(Expression const& _e); - /// Returns the z3 function declaration corresponding to the given variable. - /// The function takes one argument which is the "sequence number". - smt::Expression var(Declaration const& _decl); - - smt::SMTLib2Interface m_interface; - std::map m_currentSequenceCounter; - std::map m_z3Expressions; - std::map m_z3Variables; - ErrorReporter& m_errorReporter; - - FunctionDefinition const* m_currentFunction = nullptr; -}; - -} -} diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 8d40c3545d34..63ab1679fa70 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -30,99 +31,31 @@ #include #include -#include -#include -#include -#include - using namespace std; using namespace dev; using namespace dev::solidity::smt; - -//namespace -//{ - -//void createSubprocess(FILE*& _readPipe, FILE*& _writePipe) -//{ -// int pipe_in[2]; /* This is the pipe with wich we write to the child process. */ -// int pipe_out[2]; /* This is the pipe with wich we read from the child process. */ - -// solAssert(!pipe(pipe_in) && !pipe(pipe_out), ""); - -// /* Attempt to fork and check for errors */ -// pid_t pid = fork(); -// solAssert(pid != -1, ""); - -// if (pid) -// { -// /* The parent has the non-zero PID. */ -// _readPipe = fdopen(pipe_out[0], "r"); -// _writePipe = fdopen(pipe_in[1], "w"); -// close(pipe_out[1]); -// close(pipe_in[0]); -// } -// else -// { -// /* The child has the zero pid returned by fork*/ -// cout << "child" << endl; -// solAssert(dup2(pipe_out[1], 1) != -1, ""); -// solAssert(dup2(pipe_in[0], 0) != -1, ""); -// solAssert(close(pipe_out[0]) == 0, ""); -// solAssert(close(pipe_out[1]) == 0, ""); -// solAssert(close(pipe_in[0]) == 0, ""); -// solAssert(close(pipe_in[1]) == 0, ""); -// execl("/usr/bin/z3", "z3", "-smt2", "-in", NULL); -// exit(1); /* Only reached if execl() failed */ -// } -//} - -//} - -SMTLib2Interface::SMTLib2Interface() +SMTLib2Interface::SMTLib2Interface(ReadFile::Callback const& _readFileCallback): + m_communicator(_readFileCallback) { - // TODO using pipes did not really work, so trying it the hard way for now. -// createSubprocess(m_solverWrite, m_solverRead); -// solAssert(m_solverWrite, "Unable to start Z3"); -// solAssert(m_solverRead, "Unable to start Z3"); -// write("(set-option :produce-models true)\n(set-logic QF_LIA)\n"); reset(); } -SMTLib2Interface::~SMTLib2Interface() -{ -// if (m_solverWrite) -// { -// write("(exit)\n"); -// fflush(m_solverWrite); -// fclose(m_solverWrite); -// m_solverWrite = nullptr; -// } -// if (m_solverRead) -// { -// fclose(m_solverRead); -// m_solverRead = nullptr; -// } -} - void SMTLib2Interface::reset() { -// write("(reset)\n"); -// write("(set-option :produce-models true)\n"); m_accumulatedOutput.clear(); m_accumulatedOutput.emplace_back(); - write("(set-option :produce-models true)\n(set-logic QF_UFLIA)\n"); + write("(set-option :produce-models true)"); + write("(set-logic QF_UFLIA)"); } void SMTLib2Interface::push() { m_accumulatedOutput.emplace_back(); - //write("(push)\n"); } void SMTLib2Interface::pop() { - //write("(pop)\n"); solAssert(!m_accumulatedOutput.empty(), ""); m_accumulatedOutput.pop_back(); } @@ -136,50 +69,34 @@ Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codom (_domain == Sort::Int ? "Int" : "Bool") + ") " + (_codomain == Sort::Int ? "Int" : "Bool") + - ")\n" + ")" ); - return Expression(_name, {}); + return Expression(std::move(_name), {}); } Expression SMTLib2Interface::newInteger(string _name) { - write("(declare-const |" + _name + "| Int)\n"); - return Expression(_name, {}); + write("(declare-const |" + _name + "| Int)"); + return Expression(std::move(_name), {}); } Expression SMTLib2Interface::newBool(string _name) { - write("(declare-const |" + _name + "| Bool)\n"); - return Expression(_name, {}); + write("(declare-const |" + _name + "| Bool)"); + return Expression(std::move(_name), {}); } -void SMTLib2Interface::addAssertion(Expression _expr) +void SMTLib2Interface::addAssertion(Expression const& _expr) { - write("(assert " + _expr.toSExpr() + ")\n"); + write("(assert " + _expr.toSExpr() + ")"); } pair> SMTLib2Interface::check(vector const& _expressionsToEvaluate) { - string checks; - if (_expressionsToEvaluate.empty()) - checks = "(check-sat)\n"; - else - { - // TODO make sure these are unique - for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) - { - auto const& e = _expressionsToEvaluate.at(i); - // TODO they don't have to be ints... - checks += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; - checks += "(assert (= |EVALEXPR_" + to_string(i) + "| " + e.toSExpr() + "))\n"; - } - checks += "(check-sat)\n"; - checks += "(get-value ("; - for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) - checks += "|EVALEXPR_" + to_string(i) + "| "; - checks += "))\n"; - } - string response = communicate(boost::algorithm::join(m_accumulatedOutput, "\n") + checks); + string response = m_communicator.communicate( + boost::algorithm::join(m_accumulatedOutput, "\n") + + checkSatAndGetValuesCommand(_expressionsToEvaluate) + ); CheckResult result; // TODO proper parsing if (boost::starts_with(response, "sat\n")) @@ -189,44 +106,43 @@ pair> SMTLib2Interface::check(vector con else if (boost::starts_with(response, "unknown\n")) result = CheckResult::UNKNOWN; else - solAssert(false, "Invalid response to check-sat: " + response); + result = CheckResult::ERROR; vector values; - if (result != CheckResult::UNSAT) + if (result != CheckResult::UNSAT && result != CheckResult::ERROR) values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend()); return make_pair(result, values); } -//string SMTLib2Interface::eval(Expression _expr) -//{ -// write("(get-value (" + _expr.toSExpr() + ")\n"); -// std::string reply = communicate(); -// cout << "<-- Z3: " << reply << endl; -// // TODO parse -// return reply; -//} - void SMTLib2Interface::write(string _data) { -// cout << " --> Z3: " << _data << endl; -// solAssert(m_solverWrite, ""); -// solAssert(fputs(_data.c_str(), m_solverWrite) >= 0 || true, "EOF while communicating with Z3."); -// solAssert(fflush(m_solverWrite) == 0 || true, ""); solAssert(!m_accumulatedOutput.empty(), ""); - m_accumulatedOutput.back() += move(_data); + m_accumulatedOutput.back() += move(_data) + "\n"; } -string SMTLib2Interface::communicate(std::string const& _input) +string SMTLib2Interface::checkSatAndGetValuesCommand(vector const& _expressionsToEvaluate) { - ofstream("/tmp/z3exchange.smt2") << _input << "(exit)" << endl; - FILE* solverOutput = popen("z3 -smt2 /tmp/z3exchange.smt2", "r"); - string result; - array buffer; - while (!feof(solverOutput)) - if (fgets(buffer.data(), 127, solverOutput) != nullptr) - result += buffer.data(); - fclose(solverOutput); - return result; + string command; + if (_expressionsToEvaluate.empty()) + command = "(check-sat)\n"; + else + { + // TODO make sure these are unique + for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) + { + auto const& e = _expressionsToEvaluate.at(i); + // TODO they don't have to be ints... + command += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; + command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + e.toSExpr() + "))\n"; + } + command += "(check-sat)\n"; + command += "(get-value ("; + for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) + command += "|EVALEXPR_" + to_string(i) + "| "; + command += "))\n"; + } + + return command; } vector SMTLib2Interface::parseValues(string::const_iterator _start, string::const_iterator _end) diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index b7bab0437aab..d8c11df9cd03 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -17,10 +17,15 @@ #pragma once +#include + #include +#include #include +#include + #include #include #include @@ -35,7 +40,7 @@ namespace smt enum class CheckResult { - SAT, UNSAT, UNKNOWN + SAT, UNSAT, UNKNOWN, ERROR }; enum class Sort @@ -43,6 +48,7 @@ enum class Sort Int, Bool }; +/// C++ representation of an SMTLIB2 expression. class Expression { friend class SMTLib2Interface; @@ -62,23 +68,23 @@ class Expression friend Expression operator!(Expression _a) { - return Expression("not", _a); + return Expression("not", std::move(_a)); } friend Expression operator&&(Expression _a, Expression _b) { - return Expression("and", _a, _b); + return Expression("and", std::move(_a), std::move(_b)); } friend Expression operator||(Expression _a, Expression _b) { - return Expression("or", _a, _b); + return Expression("or", std::move(_a), std::move(_b)); } friend Expression operator==(Expression _a, Expression _b) { - return Expression("=", _a, _b); + return Expression("=", std::move(_a), std::move(_b)); } friend Expression operator!=(Expression _a, Expression _b) { - return !(_a == _b); + return !(std::move(_a) == std::move(_b)); } friend Expression operator<(Expression _a, Expression _b) { @@ -140,8 +146,7 @@ class Expression class SMTLib2Interface: public boost::noncopyable { public: - SMTLib2Interface(); - ~SMTLib2Interface(); + SMTLib2Interface(ReadFile::Callback const& _readFileCallback); void reset(); @@ -152,19 +157,17 @@ class SMTLib2Interface: public boost::noncopyable Expression newInteger(std::string _name); Expression newBool(std::string _name); - void addAssertion(Expression _expr); + void addAssertion(Expression const& _expr); std::pair> check(std::vector const& _expressionsToEvaluate); -// std::string eval(Expression _expr); private: void write(std::string _data); - std::string communicate(std::string const& _input); + std::string checkSatAndGetValuesCommand(std::vector const& _expressionsToEvaluate); std::vector parseValues(std::string::const_iterator _start, std::string::const_iterator _end); + SMTSolverCommunicator m_communicator; std::vector m_accumulatedOutput; -// FILE* m_solverWrite = nullptr; -// FILE* m_solverRead = nullptr; }; diff --git a/libsolidity/formal/SMTSolverCommunicator.cpp b/libsolidity/formal/SMTSolverCommunicator.cpp new file mode 100644 index 000000000000..a97e5fcc05fe --- /dev/null +++ b/libsolidity/formal/SMTSolverCommunicator.cpp @@ -0,0 +1,75 @@ +/* + 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 + +using namespace std; +using namespace dev; +using namespace dev::solidity::smt; + +#ifdef EMSCRIPTEN + +string SMTSolverCommunicator::communicate(string const& _input) +{ + auto result = m_readFileCallback("SMTLIB2Solver>> " + _input); + if (result.success) + return result.contentsOrErrorMessage; + else + return ""; +} + +#else + +#ifndef _WIN32 +inline FILE* _popen(char const* command, char const* type) +{ + return popen(command, type); +} +inline int _pclose(FILE* file) +{ + return pclose(file); +} +#endif + +string SMTSolverCommunicator::communicate(string const& _input) +{ + namespace fs = boost::filesystem; + auto tempPath = fs::unique_path(fs::temp_directory_path() / "%%%%-%%%%-%%%%.smt2"); + ScopeGuard s1([&]() { fs::remove(tempPath); }); + ofstream(tempPath.string()) << _input << "(exit)" << endl; + + // TODO Escaping might not be 100% perfect. + FILE* solverOutput = _popen(("z3 -smt2 \"" + tempPath.string() + "\"").c_str(), "r"); + ScopeGuard s2([&]() { _pclose(solverOutput); }); + + string result; + array buffer; + while (!feof(solverOutput)) + if (fgets(buffer.data(), 127, solverOutput) != nullptr) + result += buffer.data(); + return result; +} + +#endif diff --git a/libsolidity/formal/SMTSolverCommunicator.h b/libsolidity/formal/SMTSolverCommunicator.h new file mode 100644 index 000000000000..25aeba61ef22 --- /dev/null +++ b/libsolidity/formal/SMTSolverCommunicator.h @@ -0,0 +1,50 @@ +/* + 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 +{ +namespace smt +{ + +/// Platform-specific way to access the SMT solver. +class SMTSolverCommunicator +{ +public: + /// Creates the communicator, the read file callback is used only + /// on the emscripten platform. + SMTSolverCommunicator(ReadFile::Callback const& _readFileCallback): + m_readFileCallback(_readFileCallback) + {} + + std::string communicate(std::string const& _input); + +private: + ReadFile::Callback m_readFileCallback; +}; + + +} +} +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 4283cd992740..50e20b3d83db 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -239,7 +239,7 @@ bool CompilerStack::analyze() if (noErrors) { - SMTChecker smtChecker(m_errorReporter); + SMTChecker smtChecker(m_errorReporter, m_readFile); for (Source const* source: m_sourceOrder) smtChecker.analyze(*source->ast); } From b9015f0fb6d4a4c332746c2e503219c9f059b657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 16 Aug 2017 13:46:04 +0200 Subject: [PATCH 133/163] CMake: Remove unused scripts --- CMakeLists.txt | 3 - cmake/CMakeParseArguments.cmake | 161 --------- cmake/EthDependencies.cmake | 26 -- cmake/EthExecutableHelper.cmake | 143 -------- cmake/EthUtils.cmake | 78 ----- cmake/FindPackageHandleStandardArgs.cmake | 382 ---------------------- cmake/FindPackageMessage.cmake | 57 ---- cmake/FindSolidity.cmake | 47 --- cmake/UseDev.cmake | 31 -- cmake/UseSolidity.cmake | 32 -- 10 files changed, 960 deletions(-) delete mode 100644 cmake/CMakeParseArguments.cmake delete mode 100644 cmake/EthExecutableHelper.cmake delete mode 100644 cmake/FindPackageHandleStandardArgs.cmake delete mode 100644 cmake/FindPackageMessage.cmake delete mode 100644 cmake/FindSolidity.cmake delete mode 100644 cmake/UseDev.cmake delete mode 100644 cmake/UseSolidity.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index da14ddb77135..87a141a9ce07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,9 +22,6 @@ find_package(Threads) # Figure out what compiler and system are we using include(EthCompilerSettings) -# Include helper macros -include(EthExecutableHelper) - # Include utils include(EthUtils) diff --git a/cmake/CMakeParseArguments.cmake b/cmake/CMakeParseArguments.cmake deleted file mode 100644 index 8553f38f5f09..000000000000 --- a/cmake/CMakeParseArguments.cmake +++ /dev/null @@ -1,161 +0,0 @@ -#.rst: -# CMakeParseArguments -# ------------------- -# -# -# -# CMAKE_PARSE_ARGUMENTS( -# args...) -# -# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions -# for parsing the arguments given to that macro or function. It -# processes the arguments and defines a set of variables which hold the -# values of the respective options. -# -# The argument contains all options for the respective macro, -# i.e. keywords which can be used when calling the macro without any -# value following, like e.g. the OPTIONAL keyword of the install() -# command. -# -# The argument contains all keywords for this macro -# which are followed by one value, like e.g. DESTINATION keyword of the -# install() command. -# -# The argument contains all keywords for this -# macro which can be followed by more than one value, like e.g. the -# TARGETS or FILES keywords of the install() command. -# -# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the -# keywords listed in , and -# a variable composed of the given -# followed by "_" and the name of the respective keyword. These -# variables will then hold the respective value from the argument list. -# For the keywords this will be TRUE or FALSE. -# -# All remaining arguments are collected in a variable -# _UNPARSED_ARGUMENTS, this can be checked afterwards to see -# whether your macro was called with unrecognized parameters. -# -# As an example here a my_install() macro, which takes similar arguments -# as the real install() command: -# -# :: -# -# function(MY_INSTALL) -# set(options OPTIONAL FAST) -# set(oneValueArgs DESTINATION RENAME) -# set(multiValueArgs TARGETS CONFIGURATIONS) -# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" -# "${multiValueArgs}" ${ARGN} ) -# ... -# -# -# -# Assume my_install() has been called like this: -# -# :: -# -# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) -# -# -# -# After the cmake_parse_arguments() call the macro will have set the -# following variables: -# -# :: -# -# MY_INSTALL_OPTIONAL = TRUE -# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() -# MY_INSTALL_DESTINATION = "bin" -# MY_INSTALL_RENAME = "" (was not used) -# MY_INSTALL_TARGETS = "foo;bar" -# MY_INSTALL_CONFIGURATIONS = "" (was not used) -# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" -# -# -# -# You can then continue and process these variables. -# -# Keywords terminate lists of values, e.g. if directly after a -# one_value_keyword another recognized keyword follows, this is -# interpreted as the beginning of the new option. E.g. -# my_install(TARGETS foo DESTINATION OPTIONAL) would result in -# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION -# would be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. - -#============================================================================= -# Copyright 2010 Alexander Neundorf -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - - -if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) - return() -endif() -set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) - - -function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) - # first set all result variables to empty/FALSE - foreach(arg_name ${_singleArgNames} ${_multiArgNames}) - set(${prefix}_${arg_name}) - endforeach() - - foreach(option ${_optionNames}) - set(${prefix}_${option} FALSE) - endforeach() - - set(${prefix}_UNPARSED_ARGUMENTS) - - set(insideValues FALSE) - set(currentArgName) - - # now iterate over all arguments and fill the result variables - foreach(currentArg ${ARGN}) - list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword - list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword - list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword - - if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) - if(insideValues) - if("${insideValues}" STREQUAL "SINGLE") - set(${prefix}_${currentArgName} ${currentArg}) - set(insideValues FALSE) - elseif("${insideValues}" STREQUAL "MULTI") - list(APPEND ${prefix}_${currentArgName} ${currentArg}) - endif() - else() - list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) - endif() - else() - if(NOT ${optionIndex} EQUAL -1) - set(${prefix}_${currentArg} TRUE) - set(insideValues FALSE) - elseif(NOT ${singleArgIndex} EQUAL -1) - set(currentArgName ${currentArg}) - set(${prefix}_${currentArgName}) - set(insideValues "SINGLE") - elseif(NOT ${multiArgIndex} EQUAL -1) - set(currentArgName ${currentArg}) - set(${prefix}_${currentArgName}) - set(insideValues "MULTI") - endif() - endif() - - endforeach() - - # propagate the result variables to the caller: - foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) - set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) - endforeach() - set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) - -endfunction() diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 39ce060e8c8a..b2bcae41bd02 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -55,29 +55,3 @@ if (APPLE) endif() include_directories(BEFORE "${PROJECT_BINARY_DIR}/include") - -function(eth_use TARGET REQUIRED) - if (NOT TARGET ${TARGET}) - message(FATAL_ERROR "eth_use called for non existing target ${TARGET}") - endif() - - if (TARGET ${PROJECT_NAME}_BuildInfo.h) - add_dependencies(${TARGET} ${PROJECT_NAME}_BuildInfo.h) - endif() - - foreach(MODULE ${ARGN}) - string(REPLACE "::" ";" MODULE_PARTS "${MODULE}") - list(GET MODULE_PARTS 0 MODULE_MAIN) - list(LENGTH MODULE_PARTS MODULE_LENGTH) - if (MODULE_LENGTH GREATER 1) - list(GET MODULE_PARTS 1 MODULE_SUB) - endif() - # TODO: check if file exists if not, throws FATAL_ERROR with detailed description - get_target_property(TARGET_APPLIED ${TARGET} TARGET_APPLIED_${MODULE_MAIN}_${MODULE_SUB}) - if (NOT TARGET_APPLIED) - include(Use${MODULE_MAIN}) - set_target_properties(${TARGET} PROPERTIES TARGET_APPLIED_${MODULE_MAIN}_${MODULE_SUB} TRUE) - eth_apply(${TARGET} ${REQUIRED} ${MODULE_SUB}) - endif() - endforeach() -endfunction() diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake deleted file mode 100644 index 746ba42e1924..000000000000 --- a/cmake/EthExecutableHelper.cmake +++ /dev/null @@ -1,143 +0,0 @@ -# -# this function requires the following variables to be specified: -# ETH_VERSION -# PROJECT_NAME -# PROJECT_VERSION -# PROJECT_COPYRIGHT_YEAR -# PROJECT_VENDOR -# PROJECT_DOMAIN_SECOND -# PROJECT_DOMAIN_FIRST -# SRC_LIST -# HEADERS -# -# params: -# ICON -# - -macro(eth_add_executable EXECUTABLE) - set (extra_macro_args ${ARGN}) - set (options) - set (one_value_args ICON) - set (multi_value_args UI_RESOURCES WIN_RESOURCES) - cmake_parse_arguments (ETH_ADD_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") - - if (APPLE) - - add_executable(${EXECUTABLE} MACOSX_BUNDLE ${SRC_LIST} ${HEADERS} ${ETH_ADD_EXECUTABLE_UI_RESOURCES}) - set(PROJECT_VERSION "${ETH_VERSION}") - set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") - set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") - set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTABLE}) - set(MACOSX_BUNDLE_ICON_FILE ${ETH_ADD_EXECUTABLE_ICON}) - set_target_properties(${EXECUTABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in") - set_source_files_properties(${EXECUTABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) - set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - - else () - add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${ETH_ADD_EXECUTABLE_WIN_RESOURCES} ${SRC_LIST} ${HEADERS}) - endif() - -endmacro() - -macro(eth_simple_add_executable EXECUTABLE) - add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) - - # Apple does not support statically linked binaries on OS X. That means - # that we can only statically link against our external libraries, but - # we cannot statically link against the C++ runtime libraries and other - # platform libraries (as is possible on Windows and Alpine Linux) to produce - # an entirely transportable binary. - # - # See https://developer.apple.com/library/mac/qa/qa1118/_index.html for more info. - # - # GLIBC also appears not to support static linkage too, which probably means that - # Debian and Ubuntu will only be able to do partially-statically linked - # executables too, just like OS X. - # - # For OS X, at the time of writing, we are left with the following dynamically - # linked dependencies, of which curl and libz might still be fixable: - # - # /usr/lib/libc++.1.dylib - # /usr/lib/libSystem.B.dylib - # /usr/lib/libcurl.4.dylib - # /usr/lib/libz.1.dylib - # - if (STATIC_LINKING AND NOT APPLE) - set(CMAKE_EXE_LINKER_FLAGS "-static ${CMAKE_EXE_LINKER_FLAGS}") - set_target_properties(${EXECUTABLE} PROPERTIES LINK_SEARCH_START_STATIC 1) - set_target_properties(${EXECUTABLE} PROPERTIES LINK_SEARCH_END_STATIC 1) - endif() -endmacro() - -macro(eth_copy_dll EXECUTABLE DLL) - # dlls must be unsubstitud list variable (without ${}) in format - # optimized;path_to_dll.dll;debug;path_to_dlld.dll - if(DEFINED MSVC) - list(GET ${DLL} 1 DLL_RELEASE) - list(GET ${DLL} 3 DLL_DEBUG) - add_custom_command(TARGET ${EXECUTABLE} - PRE_BUILD - COMMAND ${CMAKE_COMMAND} ARGS - -DDLL_RELEASE="${DLL_RELEASE}" - -DDLL_DEBUG="${DLL_DEBUG}" - -DCONF="$" - -DDESTINATION="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" - -P "${ETH_SCRIPTS_DIR}/copydlls.cmake" - ) - endif() -endmacro() - -macro(eth_copy_dlls EXECUTABLE) - foreach(dll ${ARGN}) - eth_copy_dll(${EXECUTABLE} ${dll}) - endforeach(dll) -endmacro() - - -macro(eth_install_executable EXECUTABLE) - - if (APPLE) - - # TODO - Why is this different than the branch Linux below, which has the RUNTIME keyword too? - install(TARGETS ${EXECUTABLE} DESTINATION bin) - - elseif (DEFINED MSVC) - - set(COMPONENT ${EXECUTABLE}) - - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Debug/" - DESTINATION . - CONFIGURATIONS Debug - COMPONENT ${COMPONENT} - ) - - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Release/" - DESTINATION . - CONFIGURATIONS Release - COMPONENT ${COMPONENT} - ) - - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/" - DESTINATION . - CONFIGURATIONS RelWithDebInfo - COMPONENT ${COMPONENT} - ) - - else() - install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin) - endif () - -endmacro() - -macro (eth_name KEY VALUE) - if (NOT (APPLE OR WIN32)) - string(TOLOWER ${VALUE} LVALUE ) - set(${KEY} ${LVALUE}) - else() - set(${KEY} ${VALUE}) - endif() -endmacro() diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake index 68fd35d15e70..a473abcb7e85 100644 --- a/cmake/EthUtils.cmake +++ b/cmake/EthUtils.cmake @@ -22,66 +22,6 @@ macro(replace_if_different SOURCE DST) endif() endmacro() -macro(eth_add_test NAME) - - # parse arguments here - set(commands) - set(current_command "") - foreach (arg ${ARGN}) - if (arg STREQUAL "ARGS") - if (current_command) - list(APPEND commands ${current_command}) - endif() - set(current_command "") - else () - set(current_command "${current_command} ${arg}") - endif() - endforeach(arg) - list(APPEND commands ${current_command}) - - message(STATUS "test: ${NAME} | ${commands}") - - # create tests - set(index 0) - list(LENGTH commands count) - while (index LESS count) - list(GET commands ${index} test_arguments) - - set(run_test "--run_test=${NAME}") - add_test(NAME "${NAME}.${index}" COMMAND testeth ${run_test} ${test_arguments}) - - math(EXPR index "${index} + 1") - endwhile(index LESS count) - - # add target to run them - add_custom_target("test.${NAME}" - DEPENDS testeth - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -DETH_TEST_NAME="${NAME}" -DCTEST_COMMAND="${CTEST_COMMAND}" -P "${ETH_SCRIPTS_DIR}/runtest.cmake" - ) - -endmacro() - -# Creates C resources file from files -function(eth_add_resources RESOURCE_FILE OUT_FILE ETH_RES_DIR) - include("${RESOURCE_FILE}") - set(OUTPUT "${ETH_RESOURCE_LOCATION}/${ETH_RESOURCE_NAME}.hpp") - #message(FATAL_ERROR "res:! ${ETH_RESOURCE_LOCATION}") - include_directories("${ETH_RESOURCE_LOCATION}") - set(${OUT_FILE} "${OUTPUT}" PARENT_SCOPE) - - set(filenames "${RESOURCE_FILE}") - list(APPEND filenames "${ETH_SCRIPTS_DIR}/resources.cmake") - foreach(resource ${ETH_RESOURCES}) - list(APPEND filenames "${${resource}}") - endforeach(resource) - - add_custom_command(OUTPUT ${OUTPUT} - COMMAND ${CMAKE_COMMAND} -DETH_RES_FILE="${RESOURCE_FILE}" -DETH_RES_DIR="${ETH_RES_DIR}" -P "${ETH_SCRIPTS_DIR}/resources.cmake" - DEPENDS ${filenames} - ) -endfunction() - macro(eth_default_option O DEF) if (DEFINED ${O}) if (${${O}}) @@ -94,21 +34,3 @@ macro(eth_default_option O DEF) endif() endmacro() -# In Windows split repositories build we need to be checking whether or not -# Debug/Release or both versions were built for the config phase to run smoothly -macro(eth_check_library_link L) - if (${${L}_LIBRARY} AND ${${L}_LIBRARY} EQUAL "${L}_LIBRARY-NOTFOUND") - unset(${${L}_LIBRARY}) - endif() - if (${${L}_LIBRARY_DEBUG} AND ${${L}_LIBRARY_DEBUG} EQUAL "${L}_LIBRARY_DEBUG-NOTFOUND") - unset(${${L}_LIBRARY_DEBUG}) - endif() - if (${${L}_LIBRARY} AND ${${L}_LIBRARY_DEBUG}) - set(${L}_LIBRARIES optimized ${${L}_LIBRARY} debug ${${L}_LIBRARY_DEBUG}) - elseif (${${L}_LIBRARY}) - set(${L}_LIBRARIES ${${L}_LIBRARY}) - elseif (${${L}_LIBRARY_DEBUG}) - set(${L}_LIBRARIES ${${L}_LIBRARY_DEBUG}) - endif() -endmacro() - diff --git a/cmake/FindPackageHandleStandardArgs.cmake b/cmake/FindPackageHandleStandardArgs.cmake deleted file mode 100644 index 6bcf1e788b8c..000000000000 --- a/cmake/FindPackageHandleStandardArgs.cmake +++ /dev/null @@ -1,382 +0,0 @@ -#.rst: -# FindPackageHandleStandardArgs -# ----------------------------- -# -# -# -# FIND_PACKAGE_HANDLE_STANDARD_ARGS( ... ) -# -# This function is intended to be used in FindXXX.cmake modules files. -# It handles the REQUIRED, QUIET and version-related arguments to -# find_package(). It also sets the _FOUND variable. The -# package is considered found if all variables ... listed contain -# valid results, e.g. valid filepaths. -# -# There are two modes of this function. The first argument in both -# modes is the name of the Find-module where it is called (in original -# casing). -# -# The first simple mode looks like this: -# -# :: -# -# FIND_PACKAGE_HANDLE_STANDARD_ARGS( -# (DEFAULT_MSG|"Custom failure message") ... ) -# -# If the variables to are all valid, then -# _FOUND will be set to TRUE. If DEFAULT_MSG is given -# as second argument, then the function will generate itself useful -# success and error messages. You can also supply a custom error -# message for the failure case. This is not recommended. -# -# The second mode is more powerful and also supports version checking: -# -# :: -# -# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME -# [FOUND_VAR ] -# [REQUIRED_VARS ...] -# [VERSION_VAR ] -# [HANDLE_COMPONENTS] -# [CONFIG_MODE] -# [FAIL_MESSAGE "Custom failure message"] ) -# -# In this mode, the name of the result-variable can be set either to -# either _FOUND or _FOUND using the -# FOUND_VAR option. Other names for the result-variable are not -# allowed. So for a Find-module named FindFooBar.cmake, the two -# possible names are FooBar_FOUND and FOOBAR_FOUND. It is recommended -# to use the original case version. If the FOUND_VAR option is not -# used, the default is _FOUND. -# -# As in the simple mode, if through are all valid, -# _FOUND will be set to TRUE. After REQUIRED_VARS the -# variables which are required for this package are listed. Following -# VERSION_VAR the name of the variable can be specified which holds the -# version of the package which has been found. If this is done, this -# version will be checked against the (potentially) specified required -# version used in the find_package() call. The EXACT keyword is also -# handled. The default messages include information about the required -# version and the version which has been actually found, both if the -# version is ok or not. If the package supports components, use the -# HANDLE_COMPONENTS option to enable handling them. In this case, -# find_package_handle_standard_args() will report which components have -# been found and which are missing, and the _FOUND variable -# will be set to FALSE if any of the required components (i.e. not the -# ones listed after OPTIONAL_COMPONENTS) are missing. Use the option -# CONFIG_MODE if your FindXXX.cmake module is a wrapper for a -# find_package(... NO_MODULE) call. In this case VERSION_VAR will be -# set to _VERSION and the macro will automatically check whether -# the Config module was found. Via FAIL_MESSAGE a custom failure -# message can be specified, if this is not used, the default message -# will be displayed. -# -# Example for mode 1: -# -# :: -# -# find_package_handle_standard_args(LibXml2 DEFAULT_MSG -# LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) -# -# -# -# LibXml2 is considered to be found, if both LIBXML2_LIBRARY and -# LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to -# TRUE. If it is not found and REQUIRED was used, it fails with -# FATAL_ERROR, independent whether QUIET was used or not. If it is -# found, success will be reported, including the content of . On -# repeated Cmake runs, the same message won't be printed again. -# -# Example for mode 2: -# -# :: -# -# find_package_handle_standard_args(LibXslt -# FOUND_VAR LibXslt_FOUND -# REQUIRED_VARS LibXslt_LIBRARIES LibXslt_INCLUDE_DIRS -# VERSION_VAR LibXslt_VERSION_STRING) -# -# In this case, LibXslt is considered to be found if the variable(s) -# listed after REQUIRED_VAR are all valid, i.e. LibXslt_LIBRARIES and -# LibXslt_INCLUDE_DIRS in this case. The result will then be stored in -# LibXslt_FOUND . Also the version of LibXslt will be checked by using -# the version contained in LibXslt_VERSION_STRING. Since no -# FAIL_MESSAGE is given, the default messages will be printed. -# -# Another example for mode 2: -# -# :: -# -# find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) -# find_package_handle_standard_args(Automoc4 CONFIG_MODE) -# -# In this case, FindAutmoc4.cmake wraps a call to find_package(Automoc4 -# NO_MODULE) and adds an additional search directory for automoc4. Here -# the result will be stored in AUTOMOC4_FOUND. The following -# FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper -# success/error message. - -#============================================================================= -# Copyright 2007-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/CMakeParseArguments.cmake) - -# internal helper macro -macro(_FPHSA_FAILURE_MESSAGE _msg) - if (${_NAME}_FIND_REQUIRED) - message(FATAL_ERROR "${_msg}") - else () - if (NOT ${_NAME}_FIND_QUIETLY) - message(STATUS "${_msg}") - endif () - endif () -endmacro() - - -# internal helper macro to generate the failure message when used in CONFIG_MODE: -macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) - # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: - if(${_NAME}_CONFIG) - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") - else() - # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. - # List them all in the error message: - if(${_NAME}_CONSIDERED_CONFIGS) - set(configsText "") - list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) - math(EXPR configsCount "${configsCount} - 1") - foreach(currentConfigIndex RANGE ${configsCount}) - list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) - list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) - set(configsText "${configsText} ${filename} (version ${version})\n") - endforeach() - if (${_NAME}_NOT_FOUND_MESSAGE) - set(configsText "${configsText} Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") - endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") - - else() - # Simple case: No Config-file was found at all: - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") - endif() - endif() -endmacro() - - -function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) - -# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in -# new extended or in the "old" mode: - set(options CONFIG_MODE HANDLE_COMPONENTS) - set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) - set(multiValueArgs REQUIRED_VARS) - set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) - list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) - - if(${INDEX} EQUAL -1) - set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) - set(FPHSA_REQUIRED_VARS ${ARGN}) - set(FPHSA_VERSION_VAR) - else() - - CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) - - if(FPHSA_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") - endif() - - if(NOT FPHSA_FAIL_MESSAGE) - set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") - endif() - endif() - -# now that we collected all arguments, process them - - if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") - set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") - endif() - - # In config-mode, we rely on the variable _CONFIG, which is set by find_package() - # when it successfully found the config-file, including version checking: - if(FPHSA_CONFIG_MODE) - list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) - list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) - set(FPHSA_VERSION_VAR ${_NAME}_VERSION) - endif() - - if(NOT FPHSA_REQUIRED_VARS) - message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") - endif() - - list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) - - string(TOUPPER ${_NAME} _NAME_UPPER) - string(TOLOWER ${_NAME} _NAME_LOWER) - - if(FPHSA_FOUND_VAR) - if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") - set(_FOUND_VAR ${FPHSA_FOUND_VAR}) - else() - message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") - endif() - else() - set(_FOUND_VAR ${_NAME_UPPER}_FOUND) - endif() - - # collect all variables which were not found, so they can be printed, so the - # user knows better what went wrong (#6375) - set(MISSING_VARS "") - set(DETAILS "") - # check if all passed variables are valid - unset(${_FOUND_VAR}) - foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) - if(NOT ${_CURRENT_VAR}) - set(${_FOUND_VAR} FALSE) - set(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") - else() - set(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]") - endif() - endforeach() - if(NOT "${${_FOUND_VAR}}" STREQUAL "FALSE") - set(${_FOUND_VAR} TRUE) - endif() - - # component handling - unset(FOUND_COMPONENTS_MSG) - unset(MISSING_COMPONENTS_MSG) - - if(FPHSA_HANDLE_COMPONENTS) - foreach(comp ${${_NAME}_FIND_COMPONENTS}) - if(${_NAME}_${comp}_FOUND) - - if(NOT DEFINED FOUND_COMPONENTS_MSG) - set(FOUND_COMPONENTS_MSG "found components: ") - endif() - set(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}") - - else() - - if(NOT DEFINED MISSING_COMPONENTS_MSG) - set(MISSING_COMPONENTS_MSG "missing components: ") - endif() - set(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}") - - if(${_NAME}_FIND_REQUIRED_${comp}) - set(${_FOUND_VAR} FALSE) - set(MISSING_VARS "${MISSING_VARS} ${comp}") - endif() - - endif() - endforeach() - set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") - set(DETAILS "${DETAILS}[c${COMPONENT_MSG}]") - endif() - - # version handling: - set(VERSION_MSG "") - set(VERSION_OK TRUE) - set(VERSION ${${FPHSA_VERSION_VAR}}) - - # check with DEFINED here as the requested or found version may be "0" - if (DEFINED ${_NAME}_FIND_VERSION) - if(DEFINED ${FPHSA_VERSION_VAR}) - - if(${_NAME}_FIND_VERSION_EXACT) # exact version required - # count the dots in the version string - string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${VERSION}") - # add one dot because there is one dot more than there are components - string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) - if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) - # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT - # is at most 4 here. Therefore a simple lookup table is used. - if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) - set(_VERSION_REGEX "[^.]*") - elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) - set(_VERSION_REGEX "[^.]*\\.[^.]*") - elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) - set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") - else () - set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") - endif () - string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${VERSION}") - unset(_VERSION_REGEX) - if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) - set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable exact version \"${VERSION}\")") - endif () - unset(_VERSION_HEAD) - else () - if (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") - set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable exact version \"${VERSION}\")") - endif () - endif () - unset(_VERSION_DOTS) - - else() # minimum version specified: - if ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") - set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable version \"${VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") - endif () - endif() - - else() - - # if the package was not found, but a version was given, add that to the output: - if(${_NAME}_FIND_VERSION_EXACT) - set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") - else() - set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") - endif() - - endif() - else () - if(VERSION) - set(VERSION_MSG "(found version \"${VERSION}\")") - endif() - endif () - - if(VERSION_OK) - set(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]") - else() - set(${_FOUND_VAR} FALSE) - endif() - - - # print the result: - if (${_FOUND_VAR}) - FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") - else () - - if(FPHSA_CONFIG_MODE) - _FPHSA_HANDLE_FAILURE_CONFIG_MODE() - else() - if(NOT VERSION_OK) - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") - else() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") - endif() - endif() - - endif () - - set(${_FOUND_VAR} ${${_FOUND_VAR}} PARENT_SCOPE) - -endfunction() diff --git a/cmake/FindPackageMessage.cmake b/cmake/FindPackageMessage.cmake deleted file mode 100644 index a0349d3db99a..000000000000 --- a/cmake/FindPackageMessage.cmake +++ /dev/null @@ -1,57 +0,0 @@ -#.rst: -# FindPackageMessage -# ------------------ -# -# -# -# FIND_PACKAGE_MESSAGE( "message for user" "find result details") -# -# This macro is intended to be used in FindXXX.cmake modules files. It -# will print a message once for each unique find result. This is useful -# for telling the user where a package was found. The first argument -# specifies the name (XXX) of the package. The second argument -# specifies the message to display. The third argument lists details -# about the find result so that if they change the message will be -# displayed again. The macro also obeys the QUIET argument to the -# find_package command. -# -# Example: -# -# :: -# -# if(X11_FOUND) -# FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}" -# "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") -# else() -# ... -# endif() - -#============================================================================= -# Copyright 2008-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -function(FIND_PACKAGE_MESSAGE pkg msg details) - # Avoid printing a message repeatedly for the same find result. - if(NOT ${pkg}_FIND_QUIETLY) - string(REPLACE "\n" "" details "${details}") - set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) - if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") - # The message has not yet been printed. - message(STATUS "${msg}") - - # Save the find details in the cache to avoid printing the same - # message again. - set("${DETAILS_VAR}" "${details}" - CACHE INTERNAL "Details about finding ${pkg}") - endif() - endif() -endfunction() diff --git a/cmake/FindSolidity.cmake b/cmake/FindSolidity.cmake deleted file mode 100644 index 440e7d74cd8b..000000000000 --- a/cmake/FindSolidity.cmake +++ /dev/null @@ -1,47 +0,0 @@ -# Find Solidity -# -# Find the solidity includes and library -# -# This module defines -# Solidity_XXX_LIBRARIES, the libraries needed to use solidity. -# SOLIDITY_INCLUDE_DIRS - -include(EthUtils) -set(LIBS solidity;lll;solevmasm) - -set(Solidity_INCLUDE_DIRS "${SOL_DIR}") - -# if the project is a subset of main cpp-ethereum project -# use same pattern for variables as Boost uses -if ((DEFINED solidity_VERSION) OR (DEFINED cpp-ethereum_VERSION)) - - foreach (l ${LIBS}) - string(TOUPPER ${l} L) - set ("Solidity_${L}_LIBRARIES" ${l}) - endforeach() - -else() - - foreach (l ${LIBS}) - string(TOUPPER ${l} L) - find_library(Solidity_${L}_LIBRARY - NAMES ${l} - PATHS ${CMAKE_LIBRARY_PATH} - PATH_SUFFIXES "lib${l}" "${l}" "lib${l}/Debug" "lib${l}/Release" - NO_DEFAULT_PATH - ) - - set(Solidity_${L}_LIBRARIES ${Solidity_${L}_LIBRARY}) - - if (DEFINED MSVC) - find_library(Solidity_${L}_LIBRARY_DEBUG - NAMES ${l} - PATHS ${CMAKE_LIBRARY_PATH} - PATH_SUFFIXES "lib${l}/Debug" - NO_DEFAULT_PATH - ) - eth_check_library_link(Solidity_${L}) - endif() - endforeach() - -endif() diff --git a/cmake/UseDev.cmake b/cmake/UseDev.cmake deleted file mode 100644 index 68df691ae004..000000000000 --- a/cmake/UseDev.cmake +++ /dev/null @@ -1,31 +0,0 @@ -function(eth_apply TARGET REQUIRED SUBMODULE) - - # Base is where all dependencies for devcore are - if (${SUBMODULE} STREQUAL "base") - # if it's ethereum source dir, always build BuildInfo.h before - eth_use(${TARGET} ${REQUIRED} Dev::buildinfo) - - target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) - target_link_libraries(${TARGET} ${Boost_THREAD_LIBRARIES}) - target_link_libraries(${TARGET} ${Boost_RANDOM_LIBRARIES}) - target_link_libraries(${TARGET} ${Boost_FILESYSTEM_LIBRARIES}) - target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES}) - target_link_libraries(${TARGET} ${Boost_REGEX_LIBRARIES}) - - if (DEFINED MSVC) - target_link_libraries(${TARGET} ${Boost_CHRONO_LIBRARIES}) - target_link_libraries(${TARGET} ${Boost_DATE_TIME_LIBRARIES}) - endif() - - if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") - target_link_libraries(${TARGET} pthread) - endif() - - endif() - - if (${SUBMODULE} STREQUAL "soldevcore") - eth_use(${TARGET} ${REQUIRED} Dev::base) - target_link_libraries(${TARGET} soldevcore) - endif() - -endfunction() diff --git a/cmake/UseSolidity.cmake b/cmake/UseSolidity.cmake deleted file mode 100644 index 5080f71b4ae4..000000000000 --- a/cmake/UseSolidity.cmake +++ /dev/null @@ -1,32 +0,0 @@ -function(eth_apply TARGET REQUIRED SUBMODULE) - - set(SOL_DIR "${ETH_CMAKE_DIR}/.." CACHE PATH "The path to the solidity directory") - set(SOL_BUILD_DIR_NAME "build" CACHE STRING "The name of the build directory in solidity repo") - set(SOL_BUILD_DIR "${SOL_DIR}/${SOL_BUILD_DIR_NAME}") - set(CMAKE_LIBRARY_PATH ${SOL_BUILD_DIR};${CMAKE_LIBRARY_PATH}) - - find_package(Solidity) - - # Hide confusing blank dependency information when using FindSolidity on itself. - if (NOT(${MODULE_MAIN} STREQUAL Solidity)) - eth_show_dependency(SOLIDITY solidity) - endif() - - target_include_directories(${TARGET} PUBLIC ${Solidity_INCLUDE_DIRS}) - - if (${SUBMODULE} STREQUAL "solevmasm") - target_link_libraries(${TARGET} ${Solidity_SOLEVMASM_LIBRARIES} jsoncpp) - endif() - - if (${SUBMODULE} STREQUAL "lll") - eth_use(${TARGET} ${REQUIRED} Solidity::solevmasm) - target_link_libraries(${TARGET} ${Solidity_LLL_LIBRARIES}) - endif() - - if (${SUBMODULE} STREQUAL "solidity" OR ${SUBMODULE} STREQUAL "") - eth_use(${TARGET} ${REQUIRED} Dev::soldevcore Solidity::solevmasm) - target_link_libraries(${TARGET} ${Solidity_SOLIDITY_LIBRARIES}) - endif() - - target_compile_definitions(${TARGET} PUBLIC ETH_SOLIDITY) -endfunction() From c9cf24458baa77e2a2de1bedbad5040d0d83aab2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 21:04:19 +0200 Subject: [PATCH 134/163] Prepare build system for Z3. --- cmake/FindZ3.cmake | 9 ++++++ libsolidity/CMakeLists.txt | 16 ++++++++- libsolidity/formal/SMTChecker.cpp | 3 ++ scripts/install_deps.sh | 54 +++++++++++++++++-------------- 4 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 cmake/FindZ3.cmake diff --git a/cmake/FindZ3.cmake b/cmake/FindZ3.cmake new file mode 100644 index 000000000000..8f3f9ee187ac --- /dev/null +++ b/cmake/FindZ3.cmake @@ -0,0 +1,9 @@ +find_path(Z3_INCLUDE_DIR z3++.h) +find_library(Z3_LIBRARY NAMES z3 ) +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) + +if(Z3_FOUND) + set(Z3_LIBRARIES ${Z3_LIBRARY}) +endif() + diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index a88d16b8c06e..f7c1a390e048 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -2,5 +2,19 @@ file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") +find_package(Z3 QUIET) +if (${Z3_FOUND}) + include_directories(${Z3_INCLUDE_DIR}) + add_definitions(-DHAVE_Z3) + message("Z3 SMT solver FOUND.") +else() + message("Z3 SMT solver NOT found.") + list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") +endif() + add_library(solidity ${sources} ${headers}) -target_link_libraries(solidity PUBLIC evmasm devcore z3) +target_link_libraries(solidity PUBLIC evmasm devcore) + +if (${Z3_FOUND}) + target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) +endif() \ No newline at end of file diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index b9e0e8f362bc..76232c2ed50e 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -76,6 +76,9 @@ bool SMTChecker::visit(FunctionDefinition const& _function) void SMTChecker::endVisit(FunctionDefinition const&) { // TOOD we could check for "reachability", i.e. satisfiability here. + // We only handle local variables, so we clear everything. + // If we add storage variables, those should be cleared differently. + m_currentSequenceCounter.clear(); m_interface.pop(); m_currentFunction = nullptr; } diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 24cf49d5d294..760e4b807d7c 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -115,7 +115,7 @@ case $(uname -s) in echo "Installing solidity dependencies on FreeBSD." echo "ERROR - 'install_deps.sh' doesn't have FreeBSD support yet." echo "Please let us know if you see this error message, and we can work out what is missing." - echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 ;; @@ -167,6 +167,7 @@ case $(uname -s) in Debian) #Debian + install_z3="" case $(lsb_release -cs) in wheezy) #wheezy @@ -174,7 +175,7 @@ case $(uname -s) in echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get 'install_deps.sh' working for Debian Wheezy, that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." exit 1 ;; @@ -185,20 +186,19 @@ case $(uname -s) in stretch) #stretch echo "Installing solidity dependencies on Debian Stretch (9.x)." - echo "ERROR - 'install_deps.sh' doesn't have Debian Stretch support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." - echo "If you would like to get 'install_deps.sh' working for Debian Stretch, that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." - exit 1 + install_z3="libz3-dev" + ;; + buster) + #buster + echo "Installing solidity dependencies on Debian Buster (10.x)." + install_z3="libz3-dev" ;; *) #other Debian echo "Installing solidity dependencies on unknown Debian version." - echo "ERROR - Debian Jessie is the only Debian version which solidity has been tested on." - echo "If you are using a different release and would like to get 'install_deps.sh'" - echo "working for that release that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." - exit 1 + echo "ERROR - This might not work, but we are trying anyway." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev" + install_z3="libz3-dev" ;; esac @@ -211,7 +211,9 @@ case $(uname -s) in gcc \ git \ libboost-all-dev \ - unzip + unzip \ + "$install_z3" + ;; @@ -267,6 +269,7 @@ case $(uname -s) in Ubuntu) #Ubuntu + install_z3="" case $(lsb_release -cs) in trusty) #trusty @@ -287,22 +290,25 @@ case $(uname -s) in xenial) #xenial echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)." + install_z3="libz3-dev" ;; yakkety) #yakkety echo "Installing solidity dependencies on Ubuntu Yakkety Yak (16.10)." - echo "" - echo "NOTE - You are in unknown territory with this preview OS." - echo "We will need to update the Ethereum PPAs, work through build and runtime breaks, etc." - echo "See https://github.com/ethereum/webthree-umbrella/issues/624." - echo "If you would like to partner with us to work through these, that" - echo "would be fantastic. Please just comment on that issue. Thanks!" + install_z3="libz3-dev" + ;; + zesty) + #zesty + echo "Installing solidity dependencies on Ubuntu Zesty (17.04)." + install_z3="libz3-dev" ;; *) #other Ubuntu echo "ERROR - Unknown or unsupported Ubuntu version (" $(lsb_release -cs) ")" - echo "We only support Trusty, Utopic, Vivid, Wily and Xenial, with work-in-progress on Yakkety." - exit 1 + echo "ERROR - This might not work, but we are trying anyway." + echo "Please drop us a message at https://gitter.im/ethereum/solidity-dev." + echo "We only support Trusty, Utopic, Vivid, Wily, Xenial and Yakkety." + install_z3="libz3-dev" ;; esac @@ -311,7 +317,7 @@ case $(uname -s) in build-essential \ cmake \ git \ - libboost-all-dev + libboost-all-dev "$install_z3" if [ "$CI" = true ]; then # Install 'eth', for use in the Solidity Tests-over-IPC. # We will not use this 'eth', but its dependencies @@ -375,7 +381,7 @@ case $(uname -s) in echo "ERROR - Unsupported or unidentified Linux distro." echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your distro working, that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 ;; esac @@ -392,6 +398,6 @@ case $(uname -s) in echo "ERROR - Unsupported or unidentified operating system." echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your operating system working, that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." + echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." ;; esac From 4cea3d4aa44194e052520fea2f6d216675d3bd14 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 18:22:51 +0200 Subject: [PATCH 135/163] Insert abstraction layer. --- libsolidity/formal/SMTChecker.cpp | 53 ++++---- libsolidity/formal/SMTChecker.h | 4 +- libsolidity/formal/SMTLib2Interface.cpp | 21 ++- libsolidity/formal/SMTLib2Interface.h | 127 ++---------------- libsolidity/formal/SolverInterface.h | 168 ++++++++++++++++++++++++ 5 files changed, 225 insertions(+), 148 deletions(-) create mode 100644 libsolidity/formal/SolverInterface.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 76232c2ed50e..a8ad60ed6a8a 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -17,6 +17,7 @@ #include +#include #include @@ -25,7 +26,7 @@ using namespace dev; using namespace dev::solidity; SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadFile::Callback const& _readFileCallback): - m_interface(_readFileCallback), + m_interface(make_shared(_readFileCallback)), m_errorReporter(_errorReporter) { } @@ -39,7 +40,7 @@ void SMTChecker::analyze(SourceUnit const& _source) pragmaFound = true; if (pragmaFound) { - m_interface.reset(); + m_interface->reset(); m_currentSequenceCounter.clear(); _source.accept(*this); } @@ -69,7 +70,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) ); // TODO actually we probably also have to reset all local variables and similar things. m_currentFunction = &_function; - m_interface.push(); + m_interface->push(); return true; } @@ -79,7 +80,7 @@ void SMTChecker::endVisit(FunctionDefinition const&) // We only handle local variables, so we clear everything. // If we add storage variables, those should be cleared differently. m_currentSequenceCounter.clear(); - m_interface.pop(); + m_interface->pop(); m_currentFunction = nullptr; } @@ -93,7 +94,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) // TODO more checks? // TODO add restrictions about type (might be assignment from smaller type) - m_interface.addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + m_interface->addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); else m_errorReporter.warning( _varDecl.location(), @@ -123,7 +124,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) if (knownVariable(*decl)) // TODO more checks? // TODO add restrictions about type (might be assignment from smaller type) - m_interface.addAssertion(newValue(*decl) == expr(_assignment.rightHandSide())); + m_interface->addAssertion(newValue(*decl) == expr(_assignment.rightHandSide())); else m_errorReporter.warning( _assignment.location(), @@ -145,7 +146,7 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) "Assertion checker does not yet implement tules and inline arrays." ); else - m_interface.addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); + m_interface->addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); } void SMTChecker::endVisit(BinaryOperation const& _op) @@ -173,13 +174,13 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface.addAssertion(expr(*args[0])); + m_interface->addAssertion(expr(*args[0])); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); - m_interface.addAssertion(expr(*args[0])); + m_interface->addAssertion(expr(*args[0])); checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); // TODO is there something meaningful we can check here? // We can check whether the condition is always fulfilled or never fulfilled. @@ -192,7 +193,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) solAssert(decl, ""); if (dynamic_cast(_identifier.annotation().type.get())) { - m_interface.addAssertion(expr(_identifier) == currentValue(*decl)); + m_interface->addAssertion(expr(_identifier) == currentValue(*decl)); return; } else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) @@ -217,7 +218,7 @@ void SMTChecker::endVisit(Literal const& _literal) if (RationalNumberType const* rational = dynamic_cast(&type)) solAssert(!rational->isFractional(), ""); - m_interface.addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); + m_interface->addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); } else m_errorReporter.warning( @@ -264,7 +265,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) &value ); - m_interface.addAssertion(expr(_op) == value); + m_interface->addAssertion(expr(_op) == value); break; } default: @@ -292,7 +293,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) /*op == Token::GreaterThanOrEqual*/ (left >= right) ); // TODO: check that other values for op are not possible. - m_interface.addAssertion(expr(_op) == value); + m_interface->addAssertion(expr(_op) == value); } else m_errorReporter.warning( @@ -308,9 +309,9 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) if (_op.annotation().commonType->category() == Type::Category::Bool) { if (_op.getOperator() == Token::And) - m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); else - m_interface.addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); } else m_errorReporter.warning( @@ -327,8 +328,8 @@ void SMTChecker::checkCondition( smt::Expression* _additionalValue ) { - m_interface.push(); - m_interface.addAssertion(_condition); + m_interface->push(); + m_interface->addAssertion(_condition); vector expressionsToEvaluate; if (m_currentFunction) @@ -344,7 +345,7 @@ void SMTChecker::checkCondition( } smt::CheckResult result; vector values; - tie(result, values) = m_interface.check(expressionsToEvaluate); + tie(result, values) = m_interface->check(expressionsToEvaluate); switch (result) { case smt::CheckResult::SAT: @@ -380,7 +381,7 @@ void SMTChecker::checkCondition( default: solAssert(false, ""); } - m_interface.pop(); + m_interface->pop(); } void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) @@ -390,13 +391,13 @@ void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setTo solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_z3Variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; - m_z3Variables.emplace(&_varDecl, m_interface.newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_z3Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); if (_setToZero) - m_interface.addAssertion(currentValue(_varDecl) == 0); + m_interface->addAssertion(currentValue(_varDecl) == 0); else { - m_interface.addAssertion(currentValue(_varDecl) >= minValue(*intType)); - m_interface.addAssertion(currentValue(_varDecl) <= maxValue(*intType)); + m_interface->addAssertion(currentValue(_varDecl) >= minValue(*intType)); + m_interface->addAssertion(currentValue(_varDecl) <= maxValue(*intType)); } } else @@ -455,14 +456,14 @@ smt::Expression SMTChecker::expr(Expression const& _e) { if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) solAssert(!rational->isFractional(), ""); - m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); + m_z3Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; } case Type::Category::Integer: - m_z3Expressions.emplace(&_e, m_interface.newInteger(uniqueSymbol(_e))); + m_z3Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; case Type::Category::Bool: - m_z3Expressions.emplace(&_e, m_interface.newBool(uniqueSymbol(_e))); + m_z3Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); break; default: solAssert(false, "Type not implemented."); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 8c6a23270f7b..afe5897d2832 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -18,7 +18,7 @@ #pragma once #include -#include +#include #include #include @@ -85,7 +85,7 @@ class SMTChecker: private ASTConstVisitor /// The function takes one argument which is the "sequence number". smt::Expression var(Declaration const& _decl); - smt::SMTLib2Interface m_interface; + std::shared_ptr m_interface; std::map m_currentSequenceCounter; std::map m_z3Expressions; std::map m_z3Variables; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 63ab1679fa70..8cc4da664cc0 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -71,24 +71,24 @@ Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codom (_codomain == Sort::Int ? "Int" : "Bool") + ")" ); - return Expression(std::move(_name), {}); + return SolverInterface::newFunction(move(_name), _domain, _codomain); } Expression SMTLib2Interface::newInteger(string _name) { write("(declare-const |" + _name + "| Int)"); - return Expression(std::move(_name), {}); + return SolverInterface::newInteger(move(_name)); } Expression SMTLib2Interface::newBool(string _name) { write("(declare-const |" + _name + "| Bool)"); - return Expression(std::move(_name), {}); + return SolverInterface::newBool(std::move(_name)); } void SMTLib2Interface::addAssertion(Expression const& _expr) { - write("(assert " + _expr.toSExpr() + ")"); + write("(assert " + toSExpr(_expr) + ")"); } pair> SMTLib2Interface::check(vector const& _expressionsToEvaluate) @@ -114,6 +114,17 @@ pair> SMTLib2Interface::check(vector con return make_pair(result, values); } +string SMTLib2Interface::toSExpr(Expression const& _expr) +{ + if (_expr.arguments.empty()) + return _expr.name; + std::string sexpr = "(" + _expr.name; + for (auto const& arg: _expr.arguments) + sexpr += " " + toSExpr(arg); + sexpr += ")"; + return sexpr; +} + void SMTLib2Interface::write(string _data) { solAssert(!m_accumulatedOutput.empty(), ""); @@ -133,7 +144,7 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector const& _ auto const& e = _expressionsToEvaluate.at(i); // TODO they don't have to be ints... command += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; - command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + e.toSExpr() + "))\n"; + command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n"; } command += "(check-sat)\n"; command += "(get-value ("; diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index d8c11df9cd03..5755ae3f68bc 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -38,129 +39,26 @@ namespace solidity namespace smt { -enum class CheckResult -{ - SAT, UNSAT, UNKNOWN, ERROR -}; - -enum class Sort -{ - Int, Bool -}; - -/// C++ representation of an SMTLIB2 expression. -class Expression -{ - friend class SMTLib2Interface; - /// Manual constructor, should only be used by SMTLib2Interface and the class itself. - Expression(std::string _name, std::vector _arguments): - m_name(std::move(_name)), m_arguments(std::move(_arguments)) {} - -public: - Expression(size_t _number): m_name(std::to_string(_number)) {} - Expression(u256 const& _number): m_name(_number.str()) {} - Expression(bigint const& _number): m_name(_number.str()) {} - - Expression(Expression const& _other) = default; - Expression(Expression&& _other) = default; - Expression& operator=(Expression const& _other) = default; - Expression& operator=(Expression&& _other) = default; - - friend Expression operator!(Expression _a) - { - return Expression("not", std::move(_a)); - } - friend Expression operator&&(Expression _a, Expression _b) - { - return Expression("and", std::move(_a), std::move(_b)); - } - friend Expression operator||(Expression _a, Expression _b) - { - return Expression("or", std::move(_a), std::move(_b)); - } - friend Expression operator==(Expression _a, Expression _b) - { - return Expression("=", std::move(_a), std::move(_b)); - } - friend Expression operator!=(Expression _a, Expression _b) - { - return !(std::move(_a) == std::move(_b)); - } - friend Expression operator<(Expression _a, Expression _b) - { - return Expression("<", std::move(_a), std::move(_b)); - } - friend Expression operator<=(Expression _a, Expression _b) - { - return Expression("<=", std::move(_a), std::move(_b)); - } - friend Expression operator>(Expression _a, Expression _b) - { - return Expression(">", std::move(_a), std::move(_b)); - } - friend Expression operator>=(Expression _a, Expression _b) - { - return Expression(">=", std::move(_a), std::move(_b)); - } - friend Expression operator+(Expression _a, Expression _b) - { - return Expression("+", std::move(_a), std::move(_b)); - } - friend Expression operator-(Expression _a, Expression _b) - { - return Expression("-", std::move(_a), std::move(_b)); - } - friend Expression operator*(Expression _a, Expression _b) - { - return Expression("*", std::move(_a), std::move(_b)); - } - Expression operator()(Expression _a) const - { - solAssert(m_arguments.empty(), "Attempted function application to non-function."); - return Expression(m_name, _a); - } - - std::string toSExpr() const - { - if (m_arguments.empty()) - return m_name; - std::string sexpr = "(" + m_name; - for (auto const& arg: m_arguments) - sexpr += " " + arg.toSExpr(); - sexpr += ")"; - return sexpr; - } - -private: - explicit Expression(std::string _name): - Expression(std::move(_name), std::vector{}) {} - Expression(std::string _name, Expression _arg): - Expression(std::move(_name), std::vector{std::move(_arg)}) {} - Expression(std::string _name, Expression _arg1, Expression _arg2): - Expression(std::move(_name), std::vector{std::move(_arg1), std::move(_arg2)}) {} - - std::string const m_name; - std::vector const m_arguments; -}; - -class SMTLib2Interface: public boost::noncopyable +class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: SMTLib2Interface(ReadFile::Callback const& _readFileCallback); - void reset(); + void reset() override; - void push(); - void pop(); + void push() override; + void pop() override; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain); - Expression newInteger(std::string _name); - Expression newBool(std::string _name); + Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override; + Expression newInteger(std::string _name) override; + Expression newBool(std::string _name) override; - void addAssertion(Expression const& _expr); - std::pair> check(std::vector const& _expressionsToEvaluate); + void addAssertion(Expression const& _expr) override; + std::pair> check(std::vector const& _expressionsToEvaluate) override; private: + std::string toSExpr(Expression const& _expr); + void write(std::string _data); std::string checkSatAndGetValuesCommand(std::vector const& _expressionsToEvaluate); @@ -170,7 +68,6 @@ class SMTLib2Interface: public boost::noncopyable std::vector m_accumulatedOutput; }; - } } } diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h new file mode 100644 index 000000000000..e9fa5cc75c01 --- /dev/null +++ b/libsolidity/formal/SolverInterface.h @@ -0,0 +1,168 @@ +/* + 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 + +#include + +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace smt +{ + +enum class CheckResult +{ + SAT, UNSAT, UNKNOWN, ERROR +}; + +enum class Sort +{ + Int, Bool +}; + +/// C++ representation of an SMTLIB2 expression. +class Expression +{ + friend class SolverInterface; +public: + Expression(size_t _number): name(std::to_string(_number)) {} + Expression(u256 const& _number): name(_number.str()) {} + Expression(bigint const& _number): name(_number.str()) {} + + Expression(Expression const& _other) = default; + Expression(Expression&& _other) = default; + Expression& operator=(Expression const& _other) = default; + Expression& operator=(Expression&& _other) = default; + + friend Expression operator!(Expression _a) + { + return Expression("not", std::move(_a)); + } + friend Expression operator&&(Expression _a, Expression _b) + { + return Expression("and", std::move(_a), std::move(_b)); + } + friend Expression operator||(Expression _a, Expression _b) + { + return Expression("or", std::move(_a), std::move(_b)); + } + friend Expression operator==(Expression _a, Expression _b) + { + return Expression("=", std::move(_a), std::move(_b)); + } + friend Expression operator!=(Expression _a, Expression _b) + { + return !(std::move(_a) == std::move(_b)); + } + friend Expression operator<(Expression _a, Expression _b) + { + return Expression("<", std::move(_a), std::move(_b)); + } + friend Expression operator<=(Expression _a, Expression _b) + { + return Expression("<=", std::move(_a), std::move(_b)); + } + friend Expression operator>(Expression _a, Expression _b) + { + return Expression(">", std::move(_a), std::move(_b)); + } + friend Expression operator>=(Expression _a, Expression _b) + { + return Expression(">=", std::move(_a), std::move(_b)); + } + friend Expression operator+(Expression _a, Expression _b) + { + return Expression("+", std::move(_a), std::move(_b)); + } + friend Expression operator-(Expression _a, Expression _b) + { + return Expression("-", std::move(_a), std::move(_b)); + } + friend Expression operator*(Expression _a, Expression _b) + { + return Expression("*", std::move(_a), std::move(_b)); + } + Expression operator()(Expression _a) const + { + solAssert(arguments.empty(), "Attempted function application to non-function."); + return Expression(name, _a); + } + + std::string const name; + std::vector const arguments; + +private: + /// Manual constructor, should only be used by SolverInterface and this class itself. + Expression(std::string _name, std::vector _arguments): + name(std::move(_name)), arguments(std::move(_arguments)) {} + + explicit Expression(std::string _name): + Expression(std::move(_name), std::vector{}) {} + Expression(std::string _name, Expression _arg): + Expression(std::move(_name), std::vector{std::move(_arg)}) {} + Expression(std::string _name, Expression _arg1, Expression _arg2): + Expression(std::move(_name), std::vector{std::move(_arg1), std::move(_arg2)}) {} +}; + +class SolverInterface +{ +public: + virtual void reset() = 0; + + virtual void push() = 0; + virtual void pop() = 0; + + virtual Expression newFunction(std::string _name, Sort /*_domain*/, Sort /*_codomain*/) + { + // Subclasses should do something here + return Expression(std::move(_name), {}); + } + virtual Expression newInteger(std::string _name) + { + // Subclasses should do something here + return Expression(std::move(_name), {}); + } + virtual Expression newBool(std::string _name) + { + // Subclasses should do something here + return Expression(std::move(_name), {}); + } + + virtual void addAssertion(Expression const& _expr) = 0; + + virtual std::pair> + check(std::vector const& _expressionsToEvaluate) = 0; +}; + + +} +} +} From ab5e3a8f6d9b92cef521b6855bf7ab649ebf751f Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 21:08:24 +0200 Subject: [PATCH 136/163] Introduce native Z3 support. --- libsolidity/formal/Z3Interface.cpp | 179 +++++++++++++++++++++++++++++ libsolidity/formal/Z3Interface.h | 65 +++++++++++ 2 files changed, 244 insertions(+) create mode 100644 libsolidity/formal/Z3Interface.cpp create mode 100644 libsolidity/formal/Z3Interface.h diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp new file mode 100644 index 000000000000..0d59a3c77950 --- /dev/null +++ b/libsolidity/formal/Z3Interface.cpp @@ -0,0 +1,179 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity::smt; + +Z3Interface::Z3Interface(): + m_solver(m_context) +{ +} + +void Z3Interface::reset() +{ + m_constants.clear(); + m_functions.clear(); + m_solver.reset(); +} + +void Z3Interface::push() +{ + m_solver.push(); +} + +void Z3Interface::pop() +{ + m_solver.pop(); +} + +Expression Z3Interface::newFunction(string _name, Sort _domain, Sort _codomain) +{ + m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(_domain), z3Sort(_codomain))}); + return SolverInterface::newFunction(move(_name), _domain, _codomain); +} + +Expression Z3Interface::newInteger(string _name) +{ + m_constants.insert({_name, m_context.int_const(_name.c_str())}); + return SolverInterface::newInteger(move(_name)); +} + +Expression Z3Interface::newBool(string _name) +{ + m_constants.insert({_name, m_context.bool_const(_name.c_str())}); + return SolverInterface::newBool(std::move(_name)); +} + +void Z3Interface::addAssertion(Expression const& _expr) +{ + m_solver.add(toZ3Expr(_expr)); +} + +pair> Z3Interface::check(vector const& _expressionsToEvaluate) +{ + CheckResult result; + switch (m_solver.check()) + { + case z3::check_result::sat: + result = CheckResult::SAT; + break; + case z3::check_result::unsat: + result = CheckResult::UNSAT; + break; + case z3::check_result::unknown: + result = CheckResult::UNKNOWN; + break; + default: + solAssert(false, ""); + } + + vector values; + if (result != CheckResult::UNSAT) + { + z3::model m = m_solver.get_model(); + for (Expression const& e: _expressionsToEvaluate) + values.push_back(toString(m.eval(toZ3Expr(e)))); + } + return make_pair(result, values); +} + +z3::expr Z3Interface::toZ3Expr(Expression const& _expr) +{ + if (_expr.arguments.empty() && m_constants.count(_expr.name)) + return m_constants.at(_expr.name); + z3::expr_vector arguments(m_context); + for (auto const& arg: _expr.arguments) + arguments.push_back(toZ3Expr(arg)); + + static map arity{ + {"not", 1}, + {"and", 2}, + {"or", 2}, + {"=", 2}, + {"<", 2}, + {"<=", 2}, + {">", 2}, + {">=", 2}, + {"+", 2}, + {"-", 2}, + {"*", 2}, + {">=", 2} + }; + string const& n = _expr.name; + if (m_functions.count(n)) + return m_functions.at(n)(arguments); + else if (m_constants.count(n)) + { + solAssert(arguments.empty(), ""); + return m_constants.at(n); + } + else if (arguments.empty()) + { + // We assume it is an integer... + return m_context.int_val(n.c_str()); + } + + assert(arity.count(n) && arity.at(n) == arguments.size()); + if (n == "not") + return !arguments[0]; + else if (n == "and") + return arguments[0] && arguments[1]; + else if (n == "or") + return arguments[0] || arguments[1]; + else if (n == "=") + return arguments[0] == arguments[1]; + else if (n == "<") + return arguments[0] < arguments[1]; + else if (n == "<=") + return arguments[0] <= arguments[1]; + else if (n == ">") + return arguments[0] > arguments[1]; + else if (n == ">=") + return arguments[0] >= arguments[1]; + else if (n == "+") + return arguments[0] + arguments[1]; + else if (n == "-") + return arguments[0] - arguments[1]; + else if (n == "*") + return arguments[0] * arguments[1]; + // Cannot reach here. + solAssert(false, ""); + return arguments[0]; +} + +z3::sort Z3Interface::z3Sort(Sort _sort) +{ + switch (_sort) + { + case Sort::Bool: + return m_context.bool_sort(); + case Sort::Int: + return m_context.int_sort(); + default: + break; + } + solAssert(false, ""); + // Cannot be reached. + return m_context.int_sort(); +} diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h new file mode 100644 index 000000000000..44d4bb2f2aae --- /dev/null +++ b/libsolidity/formal/Z3Interface.h @@ -0,0 +1,65 @@ +/* + 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 smt +{ + +class Z3Interface: public SolverInterface, public boost::noncopyable +{ +public: + Z3Interface(); + + void reset() override; + + void push() override; + void pop() override; + + Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override; + Expression newInteger(std::string _name) override; + Expression newBool(std::string _name) override; + + void addAssertion(Expression const& _expr) override; + std::pair> check(std::vector const& _expressionsToEvaluate) override; + +private: + z3::expr toZ3Expr(Expression const& _expr); + z3::sort z3Sort(smt::Sort _sort); + + std::string checkSatAndGetValuesCommand(std::vector const& _expressionsToEvaluate); + std::vector parseValues(std::string::const_iterator _start, std::string::const_iterator _end); + + z3::context m_context; + z3::solver m_solver; + std::map m_constants; + std::map m_functions; +}; + +} +} +} From 9ac2ac14c1819be2341c6947245bf63b02795528 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 21:06:04 +0200 Subject: [PATCH 137/163] Rename read file callback. --- libsolidity/formal/SMTChecker.h | 2 +- libsolidity/formal/SMTLib2Interface.cpp | 6 ++++-- libsolidity/formal/SMTLib2Interface.h | 2 +- libsolidity/interface/CompilerStack.cpp | 8 ++++---- libsolidity/interface/CompilerStack.h | 5 +++-- libsolidity/interface/ReadFile.h | 8 ++++---- libsolidity/interface/StandardCompiler.cpp | 8 ++++---- libsolidity/interface/StandardCompiler.h | 4 ++-- solc/CommandLineInterface.cpp | 14 +++++++------- solc/jsonCompiler.cpp | 12 ++++++------ 10 files changed, 36 insertions(+), 33 deletions(-) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index afe5897d2832..f0968cc788d6 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -34,7 +34,7 @@ class ErrorReporter; class SMTChecker: private ASTConstVisitor { public: - SMTChecker(ErrorReporter& _errorReporter, ReadFile::Callback const& _readCallback); + SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readCallback); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 8cc4da664cc0..4b118abcbc88 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -33,10 +34,11 @@ using namespace std; using namespace dev; +using namespace dev::solidity; using namespace dev::solidity::smt; -SMTLib2Interface::SMTLib2Interface(ReadFile::Callback const& _readFileCallback): - m_communicator(_readFileCallback) +SMTLib2Interface::SMTLib2Interface(ReadCallback::Callback const& _queryCallback): + m_queryCallback(_queryCallback) { reset(); } diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index 5755ae3f68bc..957f2ea4fb48 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -42,7 +42,7 @@ namespace smt class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: - SMTLib2Interface(ReadFile::Callback const& _readFileCallback); + SMTLib2Interface(ReadCallback::Callback const& _queryCallback); void reset() override; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 50e20b3d83db..363f45ddde73 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -239,7 +239,7 @@ bool CompilerStack::analyze() if (noErrors) { - SMTChecker smtChecker(m_errorReporter, m_readFile); + SMTChecker smtChecker(m_errorReporter, m_smtQuery); for (Source const* source: m_sourceOrder) smtChecker.analyze(*source->ast); } @@ -535,17 +535,17 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string if (m_sources.count(importPath) || newSources.count(importPath)) continue; - ReadFile::Result result{false, string("File not supplied initially.")}; + ReadCallback::Result result{false, string("File not supplied initially.")}; if (m_readFile) result = m_readFile(importPath); if (result.success) - newSources[importPath] = result.contentsOrErrorMessage; + newSources[importPath] = result.responseOrErrorMessage; else { m_errorReporter.parserError( import->location(), - string("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage) + string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) ); continue; } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index bb0f4126cc4f..361b8a45ea9a 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -82,7 +82,7 @@ class CompilerStack: boost::noncopyable /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()): + explicit CompilerStack(ReadCallback::Callback const& _readFile = ReadCallback::Callback()): m_readFile(_readFile), m_errorList(), m_errorReporter(m_errorList) {} @@ -287,7 +287,8 @@ class CompilerStack: boost::noncopyable std::string target; }; - ReadFile::Callback m_readFile; + ReadCallback::Callback m_readFile; + ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; std::map m_libraries; diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h index 2e8a6bd8323a..7068629decce 100644 --- a/libsolidity/interface/ReadFile.h +++ b/libsolidity/interface/ReadFile.h @@ -27,17 +27,17 @@ namespace dev namespace solidity { -class ReadFile: boost::noncopyable +class ReadCallback: boost::noncopyable { public: - /// File reading result. + /// File reading or generic query result. struct Result { bool success; - std::string contentsOrErrorMessage; + std::string responseOrErrorMessage; }; - /// File reading callback. + /// File reading or generic query callback. using Callback = std::function; }; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 7a6f99893edc..be823743fead 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -203,10 +203,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) for (auto const& url: sources[sourceName]["urls"]) { - ReadFile::Result result = m_readFile(url.asString()); + ReadCallback::Result result = m_readFile(url.asString()); if (result.success) { - if (!hash.empty() && !hashMatchesContent(hash, result.contentsOrErrorMessage)) + if (!hash.empty() && !hashMatchesContent(hash, result.responseOrErrorMessage)) errors.append(formatError( false, "IOError", @@ -215,13 +215,13 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) )); else { - m_compilerStack.addSource(sourceName, result.contentsOrErrorMessage); + m_compilerStack.addSource(sourceName, result.responseOrErrorMessage); found = true; break; } } else - failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.contentsOrErrorMessage); + failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.responseOrErrorMessage); } for (auto const& failure: failures) diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index d9787a40b437..11a0b4c24b77 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -40,7 +40,7 @@ class StandardCompiler: boost::noncopyable /// Creates a new StandardCompiler. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - explicit StandardCompiler(ReadFile::Callback const& _readFile = ReadFile::Callback()) + explicit StandardCompiler(ReadCallback::Callback const& _readFile = ReadCallback::Callback()) : m_compilerStack(_readFile), m_readFile(_readFile) { } @@ -56,7 +56,7 @@ class StandardCompiler: boost::noncopyable Json::Value compileInternal(Json::Value const& _input); CompilerStack m_compilerStack; - ReadFile::Callback m_readFile; + ReadCallback::Callback m_readFile; }; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index bfc53aef5520..315f951e7deb 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -663,7 +663,7 @@ Allowed options)", bool CommandLineInterface::processInput() { - ReadFile::Callback fileReader = [this](string const& _path) + ReadCallback::Callback fileReader = [this](string const& _path) { try { @@ -683,25 +683,25 @@ bool CommandLineInterface::processInput() } } if (!isAllowed) - return ReadFile::Result{false, "File outside of allowed directories."}; + return ReadCallback::Result{false, "File outside of allowed directories."}; else if (!boost::filesystem::exists(path)) - return ReadFile::Result{false, "File not found."}; + return ReadCallback::Result{false, "File not found."}; else if (!boost::filesystem::is_regular_file(canonicalPath)) - return ReadFile::Result{false, "Not a valid file."}; + return ReadCallback::Result{false, "Not a valid file."}; else { auto contents = dev::contentsString(canonicalPath.string()); m_sourceCodes[path.string()] = contents; - return ReadFile::Result{true, contents}; + return ReadCallback::Result{true, contents}; } } catch (Exception const& _exception) { - return ReadFile::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; + return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; } catch (...) { - return ReadFile::Result{false, "Unknown exception in read callback."}; + return ReadCallback::Result{false, "Unknown exception in read callback."}; } }; diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index ab928ac0669f..684d49e4b141 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -41,9 +41,9 @@ typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, cha namespace { -ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr) +ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr) { - ReadFile::Callback readCallback; + ReadCallback::Callback readCallback; if (_readCallback) { readCallback = [=](string const& _path) @@ -51,23 +51,23 @@ ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullp char* contents_c = nullptr; char* error_c = nullptr; _readCallback(_path.c_str(), &contents_c, &error_c); - ReadFile::Result result; + ReadCallback::Result result; result.success = true; if (!contents_c && !error_c) { result.success = false; - result.contentsOrErrorMessage = "File not found."; + result.responseOrErrorMessage = "File not found."; } if (contents_c) { result.success = true; - result.contentsOrErrorMessage = string(contents_c); + result.responseOrErrorMessage = string(contents_c); free(contents_c); } if (error_c) { result.success = false; - result.contentsOrErrorMessage = string(error_c); + result.responseOrErrorMessage = string(error_c); free(error_c); } return result; From 1e05ebe50e0530beb62c96fc1112e935a5b11473 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 21:06:29 +0200 Subject: [PATCH 138/163] Refactor Z3 read callback. --- libsolidity/formal/SMTChecker.cpp | 27 ++++++- libsolidity/formal/SMTLib2Interface.cpp | 14 +++- libsolidity/formal/SMTLib2Interface.h | 6 +- libsolidity/formal/SMTSolverCommunicator.cpp | 75 -------------------- libsolidity/formal/SMTSolverCommunicator.h | 50 ------------- libsolidity/formal/SolverInterface.h | 7 +- 6 files changed, 46 insertions(+), 133 deletions(-) delete mode 100644 libsolidity/formal/SMTSolverCommunicator.cpp delete mode 100644 libsolidity/formal/SMTSolverCommunicator.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a8ad60ed6a8a..c2f5c56d715c 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -17,7 +17,11 @@ #include +#ifdef HAVE_Z3 +#include +#else #include +#endif #include @@ -25,10 +29,15 @@ using namespace std; using namespace dev; using namespace dev::solidity; -SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadFile::Callback const& _readFileCallback): +SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback): +#ifdef HAVE_Z3 + m_interface(make_shared()), +#else m_interface(make_shared(_readFileCallback)), +#endif m_errorReporter(_errorReporter) { + (void)_readFileCallback; } void SMTChecker::analyze(SourceUnit const& _source) @@ -36,7 +45,7 @@ void SMTChecker::analyze(SourceUnit const& _source) bool pragmaFound = false; for (auto const& node: _source.nodes()) if (auto const* pragma = dynamic_cast(node.get())) - if (pragma->literals()[0] == "checkAssertionsZ3") + if (pragma->literals()[0] == "checkAssertions") pragmaFound = true; if (pragmaFound) { @@ -345,7 +354,19 @@ void SMTChecker::checkCondition( } smt::CheckResult result; vector values; - tie(result, values) = m_interface->check(expressionsToEvaluate); + try + { + tie(result, values) = m_interface->check(expressionsToEvaluate); + } + catch (smt::SolverError const& _e) + { + string description("Error querying SMT solver"); + if (_e.comment()) + description += ": " + *_e.comment(); + m_errorReporter.warning(_location, description); + return; + } + switch (result) { case smt::CheckResult::SAT: diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 4b118abcbc88..e7a9ef8cbe5a 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -95,10 +95,11 @@ void SMTLib2Interface::addAssertion(Expression const& _expr) pair> SMTLib2Interface::check(vector const& _expressionsToEvaluate) { - string response = m_communicator.communicate( + string response = querySolver( boost::algorithm::join(m_accumulatedOutput, "\n") + checkSatAndGetValuesCommand(_expressionsToEvaluate) ); + CheckResult result; // TODO proper parsing if (boost::starts_with(response, "sat\n")) @@ -173,3 +174,14 @@ vector SMTLib2Interface::parseValues(string::const_iterator _start, stri return values; } + +string SMTLib2Interface::querySolver(string const& _input) +{ + if (!m_queryCallback) + BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment("No SMT solver available.")); + + ReadCallback::Result queryResult = m_queryCallback(_input); + if (!queryResult.success) + BOOST_THROW_EXCEPTION(SolverError() << errinfo_comment(queryResult.responseOrErrorMessage)); + return queryResult.responseOrErrorMessage; +} diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index 957f2ea4fb48..b8dac36628bc 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -17,7 +17,6 @@ #pragma once -#include #include #include @@ -64,7 +63,10 @@ class SMTLib2Interface: public SolverInterface, public boost::noncopyable std::string checkSatAndGetValuesCommand(std::vector const& _expressionsToEvaluate); std::vector parseValues(std::string::const_iterator _start, std::string::const_iterator _end); - SMTSolverCommunicator m_communicator; + /// Communicates with the solver via the callback. Throws SMTSolverError on error. + std::string querySolver(std::string const& _input); + + ReadCallback::Callback m_queryCallback; std::vector m_accumulatedOutput; }; diff --git a/libsolidity/formal/SMTSolverCommunicator.cpp b/libsolidity/formal/SMTSolverCommunicator.cpp deleted file mode 100644 index a97e5fcc05fe..000000000000 --- a/libsolidity/formal/SMTSolverCommunicator.cpp +++ /dev/null @@ -1,75 +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 - -using namespace std; -using namespace dev; -using namespace dev::solidity::smt; - -#ifdef EMSCRIPTEN - -string SMTSolverCommunicator::communicate(string const& _input) -{ - auto result = m_readFileCallback("SMTLIB2Solver>> " + _input); - if (result.success) - return result.contentsOrErrorMessage; - else - return ""; -} - -#else - -#ifndef _WIN32 -inline FILE* _popen(char const* command, char const* type) -{ - return popen(command, type); -} -inline int _pclose(FILE* file) -{ - return pclose(file); -} -#endif - -string SMTSolverCommunicator::communicate(string const& _input) -{ - namespace fs = boost::filesystem; - auto tempPath = fs::unique_path(fs::temp_directory_path() / "%%%%-%%%%-%%%%.smt2"); - ScopeGuard s1([&]() { fs::remove(tempPath); }); - ofstream(tempPath.string()) << _input << "(exit)" << endl; - - // TODO Escaping might not be 100% perfect. - FILE* solverOutput = _popen(("z3 -smt2 \"" + tempPath.string() + "\"").c_str(), "r"); - ScopeGuard s2([&]() { _pclose(solverOutput); }); - - string result; - array buffer; - while (!feof(solverOutput)) - if (fgets(buffer.data(), 127, solverOutput) != nullptr) - result += buffer.data(); - return result; -} - -#endif diff --git a/libsolidity/formal/SMTSolverCommunicator.h b/libsolidity/formal/SMTSolverCommunicator.h deleted file mode 100644 index 25aeba61ef22..000000000000 --- a/libsolidity/formal/SMTSolverCommunicator.h +++ /dev/null @@ -1,50 +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 - -namespace dev -{ -namespace solidity -{ -namespace smt -{ - -/// Platform-specific way to access the SMT solver. -class SMTSolverCommunicator -{ -public: - /// Creates the communicator, the read file callback is used only - /// on the emscripten platform. - SMTSolverCommunicator(ReadFile::Callback const& _readFileCallback): - m_readFileCallback(_readFileCallback) - {} - - std::string communicate(std::string const& _input); - -private: - ReadFile::Callback m_readFileCallback; -}; - - -} -} -} diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index e9fa5cc75c01..2c00d030b754 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,12 +17,11 @@ #pragma once -#include - #include #include #include +#include #include @@ -132,6 +131,8 @@ class Expression Expression(std::move(_name), std::vector{std::move(_arg1), std::move(_arg2)}) {} }; +DEV_SIMPLE_EXCEPTION(SolverError); + class SolverInterface { public: @@ -158,6 +159,8 @@ class SolverInterface virtual void addAssertion(Expression const& _expr) = 0; + /// Checks for satisfiability, evaluates the expressions if a model + /// is available. Throws SMTSolverError on error. virtual std::pair> check(std::vector const& _expressionsToEvaluate) = 0; }; From 5bfd5d98c13b57c887eb09bffb9a03f2d1726b41 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 13 Jul 2017 22:07:01 +0200 Subject: [PATCH 139/163] Format numbers more nicely. --- libdevcore/CommonData.h | 11 ++++++++++ libsolidity/formal/SMTChecker.cpp | 36 +++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 0321011e3d0a..5df8986a87f6 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -145,6 +145,17 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) return (prefix == HexPrefix::Add) ? "0x" + str : str; } +/// Returns decimal representation for small numbers and hex for large numbers. +inline std::string formatNumber(bigint const& _value) +{ + if (_value < 0) + return "-" + formatNumber(-_value); + if (_value > 0x1000000) + return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add); + else + return _value.str(); +} + inline std::string toCompactHexWithPrefix(u256 val) { std::ostringstream ret; diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index c2f5c56d715c..129822049562 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -262,14 +262,14 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) checkCondition( value < minValue(intType), _op.location(), - "Underflow (resulting value less than " + intType.minValue().str() + ")", + "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")", "value", &value ); checkCondition( value > maxValue(intType), _op.location(), - "Overflow (resulting value larger than " + intType.maxValue().str() + ")", + "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")", "value", &value ); @@ -341,16 +341,26 @@ void SMTChecker::checkCondition( m_interface->addAssertion(_condition); vector expressionsToEvaluate; + vector expressionNames; if (m_currentFunction) { if (_additionalValue) + { expressionsToEvaluate.emplace_back(*_additionalValue); + expressionNames.push_back(_additionalValueName); + } for (auto const& param: m_currentFunction->parameters()) if (knownVariable(*param)) + { expressionsToEvaluate.emplace_back(currentValue(*param)); + expressionNames.push_back(param->name()); + } for (auto const& var: m_currentFunction->localVariables()) if (knownVariable(*var)) + { expressionsToEvaluate.emplace_back(currentValue(*var)); + expressionNames.push_back(var->name()); + } } smt::CheckResult result; vector values; @@ -373,18 +383,22 @@ void SMTChecker::checkCondition( { std::ostringstream message; message << _description << " happens here"; - size_t i = 0; if (m_currentFunction) { message << " for:\n"; - if (_additionalValue) - message << " " << _additionalValueName << " = " << values.at(i++) << "\n"; - for (auto const& param: m_currentFunction->parameters()) - if (knownVariable(*param)) - message << " " << param->name() << " = " << values.at(i++) << "\n"; - for (auto const& var: m_currentFunction->localVariables()) - if (knownVariable(*var)) - message << " " << var->name() << " = " << values.at(i++) << "\n"; + solAssert(values.size() == expressionNames.size(), ""); + for (size_t i = 0; i < values.size(); ++i) + { + string formattedValue = values.at(i); + try + { + // Parse and re-format nicely + formattedValue = formatNumber(bigint(formattedValue)); + } + catch (...) { } + + message << " " << expressionNames.at(i) << " = " << formattedValue << "\n"; + } } else message << "."; From 75f09f2a84e556ca48b7bae00b459c77a0fa09fe Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 14 Jul 2017 11:49:27 +0200 Subject: [PATCH 140/163] Partial support for if statements. --- libsolidity/formal/SMTChecker.cpp | 109 +++++++++++++++++++++++---- libsolidity/formal/SMTChecker.h | 16 ++++ libsolidity/formal/SolverInterface.h | 7 ++ libsolidity/formal/Z3Interface.cpp | 12 ++- 4 files changed, 128 insertions(+), 16 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 129822049562..e1fd2bfdf68e 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -25,6 +25,8 @@ #include +#include + using namespace std; using namespace dev; using namespace dev::solidity; @@ -51,6 +53,7 @@ void SMTChecker::analyze(SourceUnit const& _source) { m_interface->reset(); m_currentSequenceCounter.clear(); + m_nextFreeSequenceCounter.clear(); _source.accept(*this); } } @@ -89,10 +92,68 @@ void SMTChecker::endVisit(FunctionDefinition const&) // We only handle local variables, so we clear everything. // If we add storage variables, those should be cleared differently. m_currentSequenceCounter.clear(); + m_nextFreeSequenceCounter.clear(); m_interface->pop(); m_currentFunction = nullptr; } +bool SMTChecker::visit(IfStatement const& _node) +{ + _node.condition().accept(*this); + + // TODO Check if condition is always true + + auto countersAtStart = m_currentSequenceCounter; + m_interface->push(); + m_interface->addAssertion(expr(_node.condition())); + _node.trueStatement().accept(*this); + auto countersAtEndOfTrue = m_currentSequenceCounter; + m_interface->pop(); + + decltype(m_currentSequenceCounter) countersAtEndOfFalse; + if (_node.falseStatement()) + { + m_currentSequenceCounter = countersAtStart; + m_interface->push(); + m_interface->addAssertion(!expr(_node.condition())); + _node.falseStatement()->accept(*this); + countersAtEndOfFalse = m_currentSequenceCounter; + m_interface->pop(); + } + else + countersAtEndOfFalse = countersAtStart; + + // Reset all values that have been touched. + + // TODO this should use a previously generated side-effect structure + + solAssert(countersAtEndOfFalse.size() == countersAtEndOfTrue.size(), ""); + for (auto const& declCounter: countersAtEndOfTrue) + { + solAssert(countersAtEndOfFalse.count(declCounter.first), ""); + auto decl = declCounter.first; + int trueCounter = countersAtEndOfTrue.at(decl); + int falseCounter = countersAtEndOfFalse.at(decl); + if (trueCounter == falseCounter) + continue; // Was not modified + newValue(*decl); + setValue(*decl, 0); + } + return false; +} + +bool SMTChecker::visit(WhileStatement const& _node) +{ + _node.condition().accept(*this); + + //m_interface->push(); + //m_interface->addAssertion(expr(_node.condition())); + // TDOO clear knowledge (increment sequence numbers and add bounds assertions ) apart from assertions + + // TODO combine similar to if + return true; +} + void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) { if (_varDecl.declarations().size() != 1) @@ -100,10 +161,13 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) _varDecl.location(), "Assertion checker does not yet support such variable declarations." ); - else if (knownVariable(*_varDecl.declarations()[0]) && _varDecl.initialValue()) - // TODO more checks? - // TODO add restrictions about type (might be assignment from smaller type) - m_interface->addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + else if (knownVariable(*_varDecl.declarations()[0])) + { + if (_varDecl.initialValue()) + // TODO more checks? + // TODO add restrictions about type (might be assignment from smaller type) + m_interface->addAssertion(newValue(*_varDecl.declarations()[0]) == expr(*_varDecl.initialValue())); + } else m_errorReporter.warning( _varDecl.location(), @@ -421,19 +485,15 @@ void SMTChecker::checkCondition( void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setToZero) { - if (auto intType = dynamic_cast(_varDecl.type().get())) + if (dynamic_cast(_varDecl.type().get())) { solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); + solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_z3Variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; + m_nextFreeSequenceCounter[&_varDecl] = 1; m_z3Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); - if (_setToZero) - m_interface->addAssertion(currentValue(_varDecl) == 0); - else - { - m_interface->addAssertion(currentValue(_varDecl) >= minValue(*intType)); - m_interface->addAssertion(currentValue(_varDecl) <= maxValue(*intType)); - } + setValue(_varDecl, _setToZero); } else m_errorReporter.warning( @@ -460,16 +520,35 @@ bool SMTChecker::knownVariable(Declaration const& _decl) smt::Expression SMTChecker::currentValue(Declaration const& _decl) { solAssert(m_currentSequenceCounter.count(&_decl), ""); - return var(_decl)(m_currentSequenceCounter.at(&_decl)); + return valueAtSequence(_decl, m_currentSequenceCounter.at(&_decl)); } -smt::Expression SMTChecker::newValue(const Declaration& _decl) +smt::Expression SMTChecker::valueAtSequence(const Declaration& _decl, int _sequence) +{ + return var(_decl)(_sequence); +} + +smt::Expression SMTChecker::newValue(Declaration const& _decl) { solAssert(m_currentSequenceCounter.count(&_decl), ""); - m_currentSequenceCounter[&_decl]++; + solAssert(m_nextFreeSequenceCounter.count(&_decl), ""); + m_currentSequenceCounter[&_decl] = m_nextFreeSequenceCounter[&_decl]++; return currentValue(_decl); } +void SMTChecker::setValue(Declaration const& _decl, bool _setToZero) +{ + auto const& intType = dynamic_cast(*_decl.type()); + + if (_setToZero) + m_interface->addAssertion(currentValue(_decl) == 0); + else + { + 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()); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index f0968cc788d6..d4935116428b 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -46,6 +46,8 @@ class SMTChecker: private ASTConstVisitor virtual void endVisit(VariableDeclaration const& _node) override; virtual bool visit(FunctionDefinition const& _node) override; virtual void endVisit(FunctionDefinition const& _node) override; + virtual bool visit(IfStatement const& _node) override; + virtual bool visit(WhileStatement const& _node) override; virtual void endVisit(VariableDeclarationStatement const& _node) override; virtual void endVisit(ExpressionStatement const& _node) override; virtual void endVisit(Assignment const& _node) override; @@ -71,10 +73,23 @@ class SMTChecker: private ASTConstVisitor std::string uniqueSymbol(Declaration const& _decl); std::string uniqueSymbol(Expression const& _expr); + + /// @returns true if _delc is a variable that is known at the current point, i.e. + /// has a valid sequence number bool knownVariable(Declaration const& _decl); + /// @returns an expression denoting the value of the variable declared in @a _decl + /// at the current point. smt::Expression currentValue(Declaration const& _decl); + /// @returns an expression denoting the value of the variable declared in @a _decl + /// at the given sequence point. Does not ensure that this sequence point exists. + smt::Expression valueAtSequence(Declaration const& _decl, int _sequence); + /// Allocates a new sequence number for the declaration, updates the current + /// sequence number to this value and returns the expression. smt::Expression newValue(Declaration const& _decl); + /// Sets the value of the declaration either to zero or to its intrinsic range. + void setValue(Declaration const& _decl, bool _setToZero); + smt::Expression minValue(IntegerType const& _t); smt::Expression maxValue(IntegerType const& _t); @@ -87,6 +102,7 @@ class SMTChecker: private ASTConstVisitor std::shared_ptr m_interface; std::map m_currentSequenceCounter; + std::map m_nextFreeSequenceCounter; std::map m_z3Expressions; std::map m_z3Variables; ErrorReporter& m_errorReporter; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 2c00d030b754..8423c4a700e8 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -61,6 +61,13 @@ class Expression Expression& operator=(Expression const& _other) = default; Expression& operator=(Expression&& _other) = default; + static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue) + { + return Expression("ite", std::vector{ + std::move(_condition), std::move(_trueValue), std::move(_falseValue) + }); + } + friend Expression operator!(Expression _a) { return Expression("not", std::move(_a)); diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 0d59a3c77950..bb0d6f6ffc5e 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -72,21 +72,28 @@ void Z3Interface::addAssertion(Expression const& _expr) pair> Z3Interface::check(vector const& _expressionsToEvaluate) { +// cout << "---------------------------------" << endl; +// cout << m_solver << endl; CheckResult result; switch (m_solver.check()) { case z3::check_result::sat: result = CheckResult::SAT; + cout << "sat" << endl; break; case z3::check_result::unsat: result = CheckResult::UNSAT; + cout << "unsat" << endl; break; case z3::check_result::unknown: result = CheckResult::UNKNOWN; + cout << "unknown" << endl; break; default: solAssert(false, ""); } +// cout << "---------------------------------" << endl; + vector values; if (result != CheckResult::UNSAT) @@ -107,6 +114,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) arguments.push_back(toZ3Expr(arg)); static map arity{ + {"ite", 3}, {"not", 1}, {"and", 2}, {"or", 2}, @@ -135,7 +143,9 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) } assert(arity.count(n) && arity.at(n) == arguments.size()); - if (n == "not") + if (n == "ite") + return z3::ite(arguments[0], arguments[1], arguments[2]); + else if (n == "not") return !arguments[0]; else if (n == "and") return arguments[0] && arguments[1]; From c93f0434cd2dc8542d9adf9dc7d9256fd54d65de Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 11 Aug 2017 18:54:56 +0200 Subject: [PATCH 141/163] Use experimental feature pragma for SMT checker. --- libsolidity/ast/ExperimentalFeatures.h | 3 +++ libsolidity/formal/SMTChecker.cpp | 7 +------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 0c03ea4ad421..2c089671222f 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -29,6 +29,7 @@ namespace solidity enum class ExperimentalFeature { + SMTChecker, ABIEncoderV2, // new ABI encoder that makes use of JULIA Test, TestOnlyAnalysis @@ -36,11 +37,13 @@ enum class ExperimentalFeature static const std::map ExperimentalFeatureOnlyAnalysis = { + { ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::TestOnlyAnalysis, true }, }; static const std::map ExperimentalFeatureNames = { + { "SMTChecker", ExperimentalFeature::SMTChecker }, { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index e1fd2bfdf68e..092ecdb2c7bd 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -44,12 +44,7 @@ SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback con void SMTChecker::analyze(SourceUnit const& _source) { - bool pragmaFound = false; - for (auto const& node: _source.nodes()) - if (auto const* pragma = dynamic_cast(node.get())) - if (pragma->literals()[0] == "checkAssertions") - pragmaFound = true; - if (pragmaFound) + if (_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker)) { m_interface->reset(); m_currentSequenceCounter.clear(); From 8853183d060104777b03921ccda1e9db600f0e8e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 11 Aug 2017 18:57:37 +0200 Subject: [PATCH 142/163] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index efcea03f2acf..ed004d8cba85 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. + * Analyzer: Experimental partial support for Z3 SMT checker. * Parser: Display previous visibility specifier in error if multiple are found. * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. From cf5e1d6120513c757bd5c71f1e3af972a9a63aeb Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Aug 2017 17:02:47 +0200 Subject: [PATCH 143/163] Review changes. --- libsolidity/formal/SMTChecker.cpp | 22 +++++++++++----------- libsolidity/formal/SMTChecker.h | 12 ++++++------ libsolidity/formal/SMTLib2Interface.cpp | 6 +++--- libsolidity/formal/SolverInterface.h | 2 +- libsolidity/formal/Z3Interface.cpp | 6 +++--- scripts/install_deps.sh | 5 +++-- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 092ecdb2c7bd..fd78e578f9ed 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -438,7 +438,7 @@ void SMTChecker::checkCondition( switch (result) { - case smt::CheckResult::SAT: + case smt::CheckResult::SATISFIABLE: { std::ostringstream message; message << _description << " happens here"; @@ -464,7 +464,7 @@ void SMTChecker::checkCondition( m_errorReporter.warning(_location, message.str()); break; } - case smt::CheckResult::UNSAT: + case smt::CheckResult::UNSATISFIABLE: break; case smt::CheckResult::UNKNOWN: m_errorReporter.warning(_location, _description + " might happen here."); @@ -484,10 +484,10 @@ void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setTo { solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_z3Variables.count(&_varDecl) == 0, ""); + solAssert(m_Variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; m_nextFreeSequenceCounter[&_varDecl] = 1; - m_z3Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); setValue(_varDecl, _setToZero); } else @@ -556,7 +556,7 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_z3Expressions.count(&_e)) + if (!m_Expressions.count(&_e)) { solAssert(_e.annotation().type, ""); switch (_e.annotation().type->category()) @@ -565,24 +565,24 @@ smt::Expression SMTChecker::expr(Expression const& _e) { if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) solAssert(!rational->isFractional(), ""); - m_z3Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; } case Type::Category::Integer: - m_z3Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; case Type::Category::Bool: - m_z3Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + m_Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); break; default: solAssert(false, "Type not implemented."); } } - return m_z3Expressions.at(&_e); + return m_Expressions.at(&_e); } smt::Expression SMTChecker::var(Declaration const& _decl) { - solAssert(m_z3Variables.count(&_decl), ""); - return m_z3Variables.at(&_decl); + solAssert(m_Variables.count(&_decl), ""); + return m_Variables.at(&_decl); } diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index d4935116428b..d23fd201b762 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -71,8 +71,8 @@ class SMTChecker: private ASTConstVisitor void createVariable(VariableDeclaration const& _varDecl, bool _setToZero); - std::string uniqueSymbol(Declaration const& _decl); - std::string uniqueSymbol(Expression const& _expr); + 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. /// has a valid sequence number @@ -90,8 +90,8 @@ class SMTChecker: private ASTConstVisitor /// Sets the value of the declaration either to zero or to its intrinsic range. void setValue(Declaration const& _decl, bool _setToZero); - smt::Expression minValue(IntegerType const& _t); - smt::Expression maxValue(IntegerType const& _t); + static smt::Expression minValue(IntegerType const& _t); + static smt::Expression maxValue(IntegerType const& _t); /// Returns the expression corresponding to the AST node. Creates a new expression /// if it does not exist yet. @@ -103,8 +103,8 @@ class SMTChecker: private ASTConstVisitor std::shared_ptr m_interface; std::map m_currentSequenceCounter; std::map m_nextFreeSequenceCounter; - std::map m_z3Expressions; - std::map m_z3Variables; + std::map m_Expressions; + std::map m_Variables; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index e7a9ef8cbe5a..cbd766fb778c 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -103,16 +103,16 @@ pair> SMTLib2Interface::check(vector con CheckResult result; // TODO proper parsing if (boost::starts_with(response, "sat\n")) - result = CheckResult::SAT; + result = CheckResult::SATISFIABLE; else if (boost::starts_with(response, "unsat\n")) - result = CheckResult::UNSAT; + result = CheckResult::UNSATISFIABLE; else if (boost::starts_with(response, "unknown\n")) result = CheckResult::UNKNOWN; else result = CheckResult::ERROR; vector values; - if (result != CheckResult::UNSAT && result != CheckResult::ERROR) + if (result != CheckResult::UNSATISFIABLE && result != CheckResult::ERROR) values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend()); return make_pair(result, values); } diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 8423c4a700e8..32d92a2a6b2c 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -39,7 +39,7 @@ namespace smt enum class CheckResult { - SAT, UNSAT, UNKNOWN, ERROR + SATISFIABLE, UNSATISFIABLE, UNKNOWN, ERROR }; enum class Sort diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index bb0d6f6ffc5e..522928f047d0 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -78,11 +78,11 @@ pair> Z3Interface::check(vector const& _ switch (m_solver.check()) { case z3::check_result::sat: - result = CheckResult::SAT; + result = CheckResult::SATISFIABLE; cout << "sat" << endl; break; case z3::check_result::unsat: - result = CheckResult::UNSAT; + result = CheckResult::UNSATISFIABLE; cout << "unsat" << endl; break; case z3::check_result::unknown: @@ -96,7 +96,7 @@ pair> Z3Interface::check(vector const& _ vector values; - if (result != CheckResult::UNSAT) + if (result != CheckResult::UNSATISFIABLE) { z3::model m = m_solver.get_model(); for (Expression const& e: _expressionsToEvaluate) diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 760e4b807d7c..3a1abe10004a 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -212,7 +212,7 @@ case $(uname -s) in git \ libboost-all-dev \ unzip \ - "$install_z3" + "$install_z3" ;; @@ -317,7 +317,8 @@ case $(uname -s) in build-essential \ cmake \ git \ - libboost-all-dev "$install_z3" + libboost-all-dev \ + "$install_z3" if [ "$CI" = true ]; then # Install 'eth', for use in the Solidity Tests-over-IPC. # We will not use this 'eth', but its dependencies From ee09a0664780a6ffd5affa27b7dcb7fc2deef608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 23 Aug 2017 17:54:53 +0200 Subject: [PATCH 144/163] CMake: Add back compatibility with CMake 3.0 --- libdevcore/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index b97866f476d2..a1c4c2d3a087 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} Threads::Threads) +target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") add_dependencies(devcore solidity_BuildInfo.h) From d57e3d7f72708bdb75d861bb8d6cc7bbe7db7263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 23 Aug 2017 17:59:48 +0200 Subject: [PATCH 145/163] CMake: Explicitly ask for Boost.System library --- cmake/EthDependencies.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 39ce060e8c8a..3d05d44b15ee 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -45,7 +45,7 @@ find_program(CTEST_COMMAND ctest) set(Boost_USE_MULTITHREADED ON) option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) -find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options) +find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system) eth_show_dependency(Boost boost) From e8d1658b583878bce1858d6dd748e83ce1450967 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 23 Aug 2017 14:21:15 +0200 Subject: [PATCH 146/163] Add external tests. --- scripts/test_emscripten.sh | 3 +++ test/externalTests.sh | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100755 test/externalTests.sh diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index a2dbe61cfb54..f6846f1c573b 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -51,3 +51,6 @@ npm version $VERSION echo "Running solc-js tests..." npm run test + +echo "Running external tests...." +"$REPO_ROOT"/test/externalTests.sh "$REPO_ROOT"/build/solc/soljson.js diff --git a/test/externalTests.sh b/test/externalTests.sh new file mode 100755 index 000000000000..1b74561b740f --- /dev/null +++ b/test/externalTests.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script to run external Solidity tests. +# +# Argument: Path to soljson.js to test. +# +# Requires npm, networking access and git to download the tests. +# +# ------------------------------------------------------------------------------ +# 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 +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +if [ ! -f "$1" ] +then + echo "Usage: $0 " + exit 1 +fi + +SOLJSON="$1" + +DIR=$(mktemp -d) +( + cd "$DIR" + echo "Running Zeppelin tests..." + git clone https://github.com/OpenZeppelin/zeppelin-solidity.git + cd zeppelin-solidity + npm install + cp "$SOLJSON" ./node_modules/solc/soljson.js + npm run test +) +rm -rf "$DIR" From a0d515c1a7bf99db88655e4b727936170f917baf Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 23 Aug 2017 21:54:22 +0100 Subject: [PATCH 147/163] Keep REPO_ROOT as absolute path --- scripts/test_emscripten.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index f6846f1c573b..f1d44a1fa521 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -28,7 +28,7 @@ set -e -REPO_ROOT="$(dirname "$0")"/.. +REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) cd $REPO_ROOT/build From b5d2a4ecd7ed971a962bac1d61b5d3ffca824084 Mon Sep 17 00:00:00 2001 From: Chim Kan Date: Wed, 23 Aug 2017 20:25:12 -0400 Subject: [PATCH 148/163] Adding storage into the pointer for Voter delegate Hi, The example from https://github.com/ethereum/solidity/blob/develop/docs/solidity-by-example.rst is not working. It keeps giving this error in the Mist program: Could not compile source code. Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning. Voter delegate = voters[to]; ^------------^ The solution is just to add the keyword "storage" and then the example works again. --- 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 dde4495b5e24..88eaadd596f2 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -126,7 +126,7 @@ of votes. // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; - Voter delegate = voters[to]; + Voter storage delegate = voters[to]; if (delegate.voted) { // If the delegate already voted, // directly add to the number of votes From 940ec4b3533dd180b51c3a41e76944d98258d9ae Mon Sep 17 00:00:00 2001 From: Leo Arias Date: Thu, 24 Aug 2017 06:20:35 +0000 Subject: [PATCH 149/163] Add libz3-dev to the snap build-packages --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index db976bc38ffe..ea0f2ef51c32 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -26,7 +26,7 @@ parts: source: . source-type: git plugin: cmake - build-packages: [build-essential, libboost-all-dev] + build-packages: [build-essential, libboost-all-dev, libz3-dev] stage-packages: [libicu55] prepare: | if git describe --exact-match --tags 2> /dev/null From 5831b048c03481fac94ef8397598070a9fc04be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 24 Aug 2017 08:31:51 +0200 Subject: [PATCH 150/163] CMake: Fix FindZ3 --- cmake/FindZ3.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmake/FindZ3.cmake b/cmake/FindZ3.cmake index 8f3f9ee187ac..971d3b4b3607 100644 --- a/cmake/FindZ3.cmake +++ b/cmake/FindZ3.cmake @@ -1,9 +1,7 @@ find_path(Z3_INCLUDE_DIR z3++.h) find_library(Z3_LIBRARY NAMES z3 ) -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) -if(Z3_FOUND) - set(Z3_LIBRARIES ${Z3_LIBRARY}) -endif() +# TODO: Create IMPORTED library for Z3. From 6dd7f6284b409355d8baf7b754af0ae9a8ee52fc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 12:04:12 +0200 Subject: [PATCH 151/163] Require libz3-dev for ubuntu PPA builds. --- scripts/release_ppa.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 4fae90efba38..cb3519b037c9 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -54,14 +54,24 @@ keyid=703F83D0 email=builds@ethereum.org packagename=solc -for distribution in trusty vivid xenial yakkety zesty +for distribution in trusty vivid xenial zesty do cd /tmp/ +rm -rf $distribution mkdir $distribution cd $distribution +# Dependency +if [ $distribution = trusty -o $distribution = vivid ] +then + Z3DEPENDENCY="" +else + Z3DEPENDENCY="libz3-dev, + " +fi + # Fetch source -git clone --recursive https://github.com/ethereum/solidity.git -b "$branch" +git clone --depth 2 --recursive https://github.com/ethereum/solidity.git -b "$branch" mv solidity solc # Fetch jsoncpp dependency @@ -102,7 +112,7 @@ Source: solc Section: science Priority: extra Maintainer: Christian (Buildserver key) -Build-Depends: debhelper (>= 9.0.0), +Build-Depends: ${Z3DEPENDENCY}debhelper (>= 9.0.0), cmake, g++-4.8, git, From f67f7988874f49049da2d32dac60bc53798706cf Mon Sep 17 00:00:00 2001 From: Nick Savers Date: Thu, 24 Aug 2017 12:15:26 +0200 Subject: [PATCH 152/163] Fix link to Russian version on wiki --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index dea11a8635b3..8c33fb9da9ab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -128,7 +128,7 @@ If you still have questions, you can try searching or asking on the site, or come to our `gitter channel `_. Ideas for improving Solidity or this documentation are always welcome! -See also `Russian version (русский перевод) `_. +See also `Russian version (русский перевод) `_. Contents ======== From d223b1361b5c8b614a5464ce5227b04d8e778e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 23 Aug 2017 14:01:51 +0200 Subject: [PATCH 153/163] CMake: Better handle -fstack-protector flag support --- cmake/EthCompilerSettings.cmake | 45 +++++++++------------------------ 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 9b294e27ff39..117dd319eb2f 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -22,6 +22,18 @@ if(CCACHE_FOUND) message("Using ccache") endif(CCACHE_FOUND) +include(CheckCXXCompilerFlag) + +check_cxx_compiler_flag(-fstack-protector-strong have_stack_protector_strong) +if (have_stack_protector_strong) + add_compile_options(-fstack-protector-strong) +else() + check_cxx_compiler_flag(-fstack-protector have_stack_protector) + if(have_stack_protector) + add_compile_options(-fstack-protector) + endif() +endif() + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Use ISO C++11 standard language. @@ -79,14 +91,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.") endif () - # Strong stack protection was only added in GCC 4.9. - # Use it if we have the option to do so. - # See https://lwn.net/Articles/584225/ - if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9) - add_compile_options(-fstack-protector-strong) - add_compile_options(-fstack-protector) - endif() - # Until https://github.com/ethereum/solidity/issues/2479 is handled # disable all implicit fallthrough warnings in the codebase for GCC > 7.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) @@ -96,31 +100,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Additional Clang-specific compiler settings. elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - add_compile_options(-fstack-protector) - - # Enable strong stack protection only on Mac and only for OS X Yosemite - # or newer (AppleClang 7.0+). We should be able to re-enable this setting - # on non-Apple Clang as well, if we can work out what expression to use for - # the version detection. - - # The fact that the version-reporting for AppleClang loses the original - # Clang versioning is rather annoying. Ideally we could just have - # a single cross-platform "if version >= 3.4.1" check. - # - # There is debug text in the else clause below, to help us work out what - # such an expression should be, if we can get this running on a Trusty box - # with Clang. Greg Colvin previously replicated the issue there too. - # - # See https://github.com/ethereum/webthree-umbrella/issues/594 - - if (APPLE) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) - add_compile_options(-fstack-protector-strong) - endif() - else() - message(WARNING "CMAKE_CXX_COMPILER_VERSION = ${CMAKE_CXX_COMPILER_VERSION}") - endif() - # A couple of extra warnings suppressions which we seemingly # need when building with Clang. # From 5668377c721c48f03518a02d0b3e45b5b61a52f6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 02:26:24 +0100 Subject: [PATCH 154/163] Introduce pure specifier on functions --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 2 ++ libsolidity/ast/ASTEnums.h | 4 +++- libsolidity/interface/ABI.cpp | 2 +- libsolidity/parsing/Parser.cpp | 2 ++ libsolidity/parsing/Token.h | 4 ++-- test/libsolidity/SolidityParser.cpp | 10 ++++++++++ 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1ea3f6c8711b..fbbf07615670 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.16 (unreleased) Features: + * Introduce ``pure`` functions. The pureness is not enforced yet, use with care. * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. * Analyzer: Experimental partial support for Z3 SMT checker. * Parser: Display previous visibility specifier in error if multiple are found. diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index ab1cbb5268c8..2f1304146a65 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -57,6 +57,8 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) solAssert(m_localVarUseCount.empty(), ""); m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); m_constructor = _function.isConstructor(); + if (_function.stateMutability() == StateMutability::Pure) + m_errorReporter.warning(_function.location(), "Function is marked pure. Be careful, pureness is not enforced yet."); return true; } diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index f7c75878eaa9..5ba21907f6c1 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -31,12 +31,14 @@ namespace solidity { // How a function can mutate the EVM state. -enum class StateMutability { View, NonPayable, Payable }; +enum class StateMutability { Pure, View, NonPayable, Payable }; inline std::string stateMutabilityToString(StateMutability const& _stateMutability) { switch(_stateMutability) { + case StateMutability::Pure: + return "pure"; case StateMutability::View: return "view"; case StateMutability::NonPayable: diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 6e8563f7b160..dd56ff6d7a2a 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -36,7 +36,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) method["type"] = "function"; method["name"] = it.second->declaration().name(); // TODO: deprecate constant in a future release - method["constant"] = it.second->stateMutability() == StateMutability::View; + method["constant"] = it.second->stateMutability() == StateMutability::Pure || it.second->stateMutability() == StateMutability::View; method["payable"] = it.second->isPayable(); method["statemutability"] = stateMutabilityToString(it.second->stateMutability()); method["inputs"] = formatTypeList( diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cd0d6157f939..ddfdb6672e3b 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -321,6 +321,8 @@ StateMutability Parser::parseStateMutability(Token::Value _token) // FIXME: constant should be removed at the next breaking release else if (_token == Token::View || _token == Token::Constant) stateMutability = StateMutability::View; + else if (_token == Token::Pure) + stateMutability = StateMutability::Pure; else solAssert(false, "Invalid state mutability specifier."); m_scanner->next(); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 3bc52f1d5389..805fbf5d2d01 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -169,6 +169,7 @@ namespace solidity K(Public, "public", 0) \ K(Pragma, "pragma", 0) \ K(Private, "private", 0) \ + K(Pure, "pure", 0) \ K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Storage, "storage", 0) \ @@ -230,7 +231,6 @@ namespace solidity K(Match, "match", 0) \ K(NullLiteral, "null", 0) \ K(Of, "of", 0) \ - K(Pure, "pure", 0) \ K(Relocatable, "relocatable", 0) \ K(Static, "static", 0) \ K(Switch, "switch", 0) \ @@ -290,7 +290,7 @@ class Token static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } - static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == View || op == Payable; } + static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 8e84ead1789e..a39e0958ac7d 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -928,6 +928,16 @@ BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers) function f() payable constant {} })"; CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\"."); + text = R"( + contract c { + function f() pure payable {} + })"; + CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\"."); + text = R"( + contract c { + function f() pure constant {} + })"; + CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\"."); } BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) From e9a9a07d94d35fd9b84b16b7bd4bf8ab0b396d22 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 15 Aug 2017 19:13:05 +0100 Subject: [PATCH 155/163] Add ABI test for pure function --- Changelog.md | 2 +- test/libsolidity/SolidityABIJSON.cpp | 55 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index fbbf07615670..8c540a284c82 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ Features: * Introduce ``pure`` functions. The pureness is not enforced yet, use with care. - * ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``. + * ABI JSON: Include new field ``statemutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``. * Analyzer: Experimental partial support for Z3 SMT checker. * Parser: Display previous visibility specifier in error if multiple are found. * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 12fb1f9c185a..dd51d92698d5 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -361,6 +361,61 @@ BOOST_AUTO_TEST_CASE(constant_function) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(pure_function) +{ + char const* sourceCode = R"( + contract test { + function foo(uint a, uint b) returns(uint d) { return a + b; } + function boo(uint32 a) pure returns(uint b) { return a * 4; } + } + )"; + + char const* interface = R"([ + { + "name": "foo", + "constant": false, + "payable" : false, + "statemutability": "nonpayable", + "type": "function", + "inputs": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + }, + { + "name": "boo", + "constant": true, + "payable" : false, + "statemutability": "pure", + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint32" + }], + "outputs": [ + { + "name": "b", + "type": "uint256" + } + ] + } + ])"; + + checkInterface(sourceCode, interface); +} + BOOST_AUTO_TEST_CASE(events) { char const* sourceCode = R"( From 93e6e83093ecd4a31035e7b6a62adabd3cd81c5a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 20:51:47 +0100 Subject: [PATCH 156/163] Document pure functions --- docs/contracts.rst | 23 ++++++++++++++++++++++- docs/miscellaneous.rst | 5 +++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 0f1a882c69e0..50e7f3d1fb0c 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -475,7 +475,7 @@ Functions can be declared ``view`` in which case they promise not to modify the contract C { function f(uint a, uint b) view returns (uint) { - return a * (b + 42); + return a * (b + 42) + now; } } @@ -488,6 +488,27 @@ Functions can be declared ``view`` in which case they promise not to modify the .. warning:: The compiler does not enforce yet that a ``view`` method is not modifying state. +.. _pure-functions: + +************** +Pure Functions +************** + +Functions can be declared ``pure`` in which case they promise not to read from or modify the state. + +:: + + pragma solidity ^0.4.0; + + contract C { + function f(uint a, uint b) pure returns (uint) { + return a * (b + 42); + } + } + +.. warning:: + The compiler does not enforce yet that a ``pure`` method is not reading from the state. + .. index:: ! fallback function, function;fallback .. _fallback-function: diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7889fff2d0e0..e78c4807a1fb 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -500,12 +500,13 @@ Function Visibility Specifiers - ``internal``: only visible internally -.. index:: modifiers, constant, anonymous, indexed +.. index:: modifiers, pure, view, payable, constant, anonymous, indexed Modifiers ========= -- ``view`` for functions: Disallow modification of state - this is not enforced yet. +- ``pure`` for functions: Disallows modification or access of state - this is not enforced yet. +- ``view`` for functions: Disallows modification of state - this is not enforced yet. - ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. - ``constant`` for functions: Same as ``view``. From 504e6285f3b9f688f4456607980fabc60d5432c2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 20:53:02 +0100 Subject: [PATCH 157/163] Add pure to ABI spec --- 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 159bd6c7ce5c..036b1ac80c09 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -295,7 +295,7 @@ The JSON format for a contract's interface is given by an array of function and/ - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; - `constant`: `true` if function is :ref:`specified to not modify blockchain state `); - `payable`: `true` if function accepts ether, defaults to `false`; -- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). +- `statemutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). `type` can be omitted, defaulting to `"function"`. From 23c791e4df32315b9f16449abaac0835be7f7fdc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 16 Aug 2017 20:53:46 +0100 Subject: [PATCH 158/163] Add pure to grammar --- docs/grammar.txt | 2 +- docs/types.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/grammar.txt b/docs/grammar.txt index 36ba36f0081b..041728c566ff 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -53,7 +53,7 @@ ArrayTypeName = TypeName '[' Expression? ']' FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )* ( 'returns' TypeNameList )? StorageLocation = 'memory' | 'storage' -StateMutability = 'constant' | 'view' | 'payable' +StateMutability = 'pure' | 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | diff --git a/docs/types.rst b/docs/types.rst index 9c1c73c67da4..fb88b0067741 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -337,7 +337,7 @@ be passed via and returned from external function calls. Function types are notated as follows:: - function () {internal|external} [constant|view|payable] [returns ()] + function () {internal|external} [pure|constant|view|payable] [returns ()] In contrast to the parameter types, the return types cannot be empty - if the function type should not return anything, the whole ``returns ()`` From f646247dfb7d398255b3e2cc1aa6e25fe8424af3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 24 Aug 2017 14:13:29 +0100 Subject: [PATCH 159/163] Consider pure as constant for the AST JSON (to aid static analyzers) --- libsolidity/ast/ASTJsonConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f1f6770e3416..c0d635f33db0 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -325,7 +325,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) std::vector> attributes = { make_pair("name", _node.name()), // FIXME: remove with next breaking release - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), @@ -418,7 +418,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), // FIXME: remove with next breaking release - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View), + make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) From deeac7e2e0a1b8ffd0bbc709264d7bf37eb05536 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 24 Aug 2017 15:23:00 +0100 Subject: [PATCH 160/163] Rename statemutability to stateMutability in ABI/AST --- Changelog.md | 2 +- docs/abi-spec.rst | 2 +- libsolidity/ast/ASTJsonConverter.cpp | 4 +- libsolidity/interface/ABI.cpp | 6 +-- test/libsolidity/SolidityABIJSON.cpp | 56 ++++++++++++++-------------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8c540a284c82..4144c264654e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ Features: * Introduce ``pure`` functions. The pureness is not enforced yet, use with care. - * ABI JSON: Include new field ``statemutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``. + * ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``. * Analyzer: Experimental partial support for Z3 SMT checker. * Parser: Display previous visibility specifier in error if multiple are found. * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 036b1ac80c09..c0969cae369d 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -295,7 +295,7 @@ The JSON format for a contract's interface is given by an array of function and/ - `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything; - `constant`: `true` if function is :ref:`specified to not modify blockchain state `); - `payable`: `true` if function accepts ether, defaults to `false`; -- `statemutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). +- `stateMutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state `), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above). `type` can be omitted, defaulting to `"function"`. diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index c0d635f33db0..afc53bfe76f3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -327,7 +327,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), - make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), + make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("isConstructor", _node.isConstructor()), @@ -416,7 +416,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) setJsonNode(_node, "FunctionTypeName", { make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("statemutability", stateMutabilityToString(_node.stateMutability())), + make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index dd56ff6d7a2a..3df9d1f8fde3 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -38,7 +38,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) // TODO: deprecate constant in a future release method["constant"] = it.second->stateMutability() == StateMutability::Pure || it.second->stateMutability() == StateMutability::View; method["payable"] = it.second->isPayable(); - method["statemutability"] = stateMutabilityToString(it.second->stateMutability()); + method["stateMutability"] = stateMutabilityToString(it.second->stateMutability()); method["inputs"] = formatTypeList( externalFunctionType->parameterNames(), externalFunctionType->parameterTypes(), @@ -58,7 +58,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType(); solAssert(!!externalFunction, ""); method["payable"] = externalFunction->isPayable(); - method["statemutability"] = stateMutabilityToString(externalFunction->stateMutability()); + method["stateMutability"] = stateMutabilityToString(externalFunction->stateMutability()); method["inputs"] = formatTypeList( externalFunction->parameterNames(), externalFunction->parameterTypes(), @@ -73,7 +73,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) Json::Value method; method["type"] = "fallback"; method["payable"] = externalFunctionType->isPayable(); - method["statemutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); + method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); abi.append(method); } for (auto const& it: _contractDef.interfaceEvents()) diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index dd51d92698d5..0512ba1fd6b6 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(basic_test) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) "name": "g", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(multiple_params) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) "name": "c", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(view_function) "name": "foo", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(view_function) "name": "boo", "constant": true, "payable" : false, - "statemutability": "view", + "stateMutability": "view", "type": "function", "inputs": [{ "name": "a", @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(constant_function) "name": "foo", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -343,7 +343,7 @@ BOOST_AUTO_TEST_CASE(constant_function) "name": "boo", "constant": true, "payable" : false, - "statemutability": "view", + "stateMutability": "view", "type": "function", "inputs": [{ "name": "a", @@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(pure_function) "name": "foo", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE(pure_function) "name": "boo", "constant": true, "payable" : false, - "statemutability": "pure", + "stateMutability": "pure", "type": "function", "inputs": [{ "name": "a", @@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(events) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -512,7 +512,7 @@ BOOST_AUTO_TEST_CASE(inherited) "name": "baseFunction", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [{ @@ -529,7 +529,7 @@ BOOST_AUTO_TEST_CASE(inherited) "name": "derivedFunction", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [{ @@ -585,7 +585,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -628,7 +628,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) "name": "f", "constant": false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "function", "inputs": [ { @@ -672,7 +672,7 @@ BOOST_AUTO_TEST_CASE(constructor_abi) } ], "payable": false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "constructor" } ])"; @@ -704,7 +704,7 @@ BOOST_AUTO_TEST_CASE(payable_constructor_abi) } ], "payable": true, - "statemutability": "payable", + "stateMutability": "payable", "type": "constructor" } ])"; @@ -730,7 +730,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) { "constant" : false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "inputs" : [], "name" : "ret", "outputs" : [ @@ -749,7 +749,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) } ], "payable": false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type": "constructor" } ] @@ -771,7 +771,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays) { "constant" : false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "name": "f", "inputs": [ { "name": "a", "type": "string" }, @@ -800,7 +800,7 @@ BOOST_AUTO_TEST_CASE(library_function) { "constant" : false, "payable" : false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "name": "f", "inputs": [ { "name": "b", "type": "test.StructType storage" }, @@ -830,7 +830,7 @@ BOOST_AUTO_TEST_CASE(include_fallback_function) [ { "payable": false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "type" : "fallback" } ] @@ -852,7 +852,7 @@ BOOST_AUTO_TEST_CASE(payable_function) { "constant" : false, "payable": false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "inputs": [], "name": "f", "outputs": [], @@ -861,7 +861,7 @@ BOOST_AUTO_TEST_CASE(payable_function) { "constant" : false, "payable": true, - "statemutability": "payable", + "stateMutability": "payable", "inputs": [], "name": "g", "outputs": [], @@ -884,7 +884,7 @@ BOOST_AUTO_TEST_CASE(payable_fallback_function) [ { "payable": true, - "statemutability": "payable", + "stateMutability": "payable", "type" : "fallback" } ] @@ -905,7 +905,7 @@ BOOST_AUTO_TEST_CASE(function_type) { "constant" : false, "payable": false, - "statemutability": "nonpayable", + "stateMutability": "nonpayable", "inputs": [{ "name": "x", "type": "function" From efe9d55ab2a55c0d7465a3de51acb3a7173a1d1a Mon Sep 17 00:00:00 2001 From: Chim Kan Date: Thu, 24 Aug 2017 12:03:27 -0400 Subject: [PATCH 161/163] Update on the example for BlindAction In the Mist app, the Blind Action contract cannot compile because it cannot accept implicit conversion of integer to byte32. I just added the conversion method byte32 in line 470 for bid.blindedBid. --- 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 88eaadd596f2..ca6b970c9896 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -467,7 +467,7 @@ high or low invalid bids. } // Make it impossible for the sender to re-claim // the same deposit. - bid.blindedBid = 0; + bid.blindedBid = bytes32(0); } msg.sender.transfer(refund); } From 0878eaad468719038754398259ff06c7004d58e9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 11:36:48 +0200 Subject: [PATCH 162/163] Prepare 0.4.16 release. --- Changelog.md | 17 +++++++++++------ docs/bugs_by_version.json | 4 ++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4144c264654e..d435a9392b42 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,14 +1,19 @@ -### 0.4.16 (unreleased) +### 0.4.16 (2017-08-24) Features: - * Introduce ``pure`` functions. The pureness is not enforced yet, use with care. - * ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``. + * ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``, + ``nonpayable`` and ``payable``. * Analyzer: Experimental partial support for Z3 SMT checker. + * Build System: Shared libraries (``libdevcore``, ``libevmasm``, ``libsolidity`` + and ``liblll``) are no longer produced during the build process. + * Metadata: Store experimental flag in metadata CBOR. * Parser: Display previous visibility specifier in error if multiple are found. - * Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``). - * Syntax Checker: Support ``pragma experimental ;`` to turn on experimental features. + * Parser: Introduce ``pure`` and ``view`` keyword for functions, + ``constant`` remains an alias for ``view`` and pureness is not enforced yet, + so use with care. * Static Analyzer: Warn about large storage structures. - * Metadata: Store experimental flag in metadata CBOR. + * Syntax Checker: Support ``pragma experimental ;`` to turn on + experimental features. * Type Checker: More detailed error message for invalid overrides. * Type Checker: Warn about shifting a literal. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 33f7bae98fdb..ea242085550c 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -354,6 +354,10 @@ "bugs": [], "released": "2017-08-08" }, + "0.4.16": { + "bugs": [], + "released": "2017-08-24" + }, "0.4.2": { "bugs": [ "DelegateCallReturnValue", From 29ba351a731ec7b9cc36bac519deed549c8fe849 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 18:37:39 +0200 Subject: [PATCH 163/163] Describe experimental features. --- Changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d435a9392b42..b78742063ceb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,9 +3,11 @@ Features: * ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``. - * Analyzer: Experimental partial support for Z3 SMT checker. + * Analyzer: Experimental partial support for Z3 SMT checker ("SMTChecker"). * Build System: Shared libraries (``libdevcore``, ``libevmasm``, ``libsolidity`` and ``liblll``) are no longer produced during the build process. + * Code generator: Experimental new implementation of ABI encoder that can + encode arbitrarily nested arrays ("ABIEncoderV2") * Metadata: Store experimental flag in metadata CBOR. * Parser: Display previous visibility specifier in error if multiple are found. * Parser: Introduce ``pure`` and ``view`` keyword for functions,