aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-08-25 02:50:37 +0800
committerGitHub <noreply@github.com>2017-08-25 02:50:37 +0800
commitd7661dd97460250b4e1127b9e7ea91e116143780 (patch)
tree0d909e7aeec373b0559b9d80f8ae9a9e03bdd92b
parentbbb8e64fbee632d1594f6c132b1174591b961207 (diff)
parentdd67e5966f41be0b2ef52041ea726930af74f7e9 (diff)
downloaddexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar.gz
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar.bz2
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar.lz
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar.xz
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.tar.zst
dexon-solidity-d7661dd97460250b4e1127b9e7ea91e116143780.zip
Merge pull request #2802 from ethereum/develop
Merge develop into release for 0.4.16
-rw-r--r--CMakeLists.txt9
-rw-r--r--Changelog.md30
-rw-r--r--circle.yml10
-rw-r--r--cmake/CMakeParseArguments.cmake161
-rw-r--r--cmake/EthCompilerSettings.cmake65
-rw-r--r--cmake/EthDependencies.cmake83
-rw-r--r--cmake/EthExecutableHelper.cmake143
-rw-r--r--cmake/EthUtils.cmake78
-rw-r--r--cmake/FindPackageHandleStandardArgs.cmake382
-rw-r--r--cmake/FindPackageMessage.cmake57
-rw-r--r--cmake/FindSolidity.cmake47
-rw-r--r--cmake/FindZ3.cmake7
-rw-r--r--cmake/UseDev.cmake31
-rw-r--r--cmake/UseSolidity.cmake32
-rw-r--r--docs/abi-spec.rst7
-rw-r--r--docs/assembly.rst41
-rw-r--r--docs/bugs_by_version.json4
-rw-r--r--docs/contracts.rst46
-rw-r--r--docs/frequently-asked-questions.rst2
-rw-r--r--docs/grammar.txt11
-rw-r--r--docs/index.rst2
-rw-r--r--docs/installing-solidity.rst8
-rw-r--r--docs/introduction-to-smart-contracts.rst4
-rw-r--r--docs/miscellaneous.rst10
-rw-r--r--docs/solidity-by-example.rst4
-rw-r--r--docs/types.rst13
-rw-r--r--docs/utils/SolidityLexer.py4
-rw-r--r--libdevcore/CMakeLists.txt22
-rw-r--r--libdevcore/Common.h3
-rw-r--r--libdevcore/CommonData.h21
-rw-r--r--libdevcore/FixedHash.h4
-rw-r--r--libdevcore/IndentedWriter.cpp65
-rw-r--r--libdevcore/IndentedWriter.h67
-rw-r--r--libdevcore/Whiskers.cpp2
-rw-r--r--libevmasm/Assembly.cpp7
-rw-r--r--libevmasm/AssemblyItem.cpp12
-rw-r--r--libevmasm/BlockDeduplicator.h2
-rw-r--r--libevmasm/CMakeLists.txt17
-rw-r--r--libevmasm/CommonSubexpressionEliminator.cpp1
-rw-r--r--libevmasm/CommonSubexpressionEliminator.h4
-rw-r--r--libevmasm/ConstantOptimiser.cpp10
-rw-r--r--libevmasm/ConstantOptimiser.h24
-rw-r--r--libevmasm/PathGasMeter.h2
-rw-r--r--liblll/CMakeLists.txt21
-rw-r--r--liblll/CodeFragment.h4
-rw-r--r--liblll/CompilerState.cpp2
-rw-r--r--liblll/CompilerState.h2
-rw-r--r--libsolidity/CMakeLists.txt35
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp44
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h3
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp43
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp91
-rw-r--r--libsolidity/analysis/TypeChecker.h3
-rw-r--r--libsolidity/ast/AST.cpp11
-rw-r--r--libsolidity/ast/AST.h50
-rw-r--r--libsolidity/ast/ASTAnnotations.h3
-rw-r--r--libsolidity/ast/ASTEnums.h54
-rw-r--r--libsolidity/ast/ASTForward.h1
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp80
-rw-r--r--libsolidity/ast/ASTJsonConverter.h21
-rw-r--r--libsolidity/ast/ASTPrinter.cpp14
-rw-r--r--libsolidity/ast/ASTPrinter.h4
-rw-r--r--libsolidity/ast/ASTVisitor.h4
-rw-r--r--libsolidity/ast/AST_accept.h12
-rw-r--r--libsolidity/ast/ExperimentalFeatures.h53
-rw-r--r--libsolidity/ast/Types.cpp155
-rw-r--r--libsolidity/ast/Types.h27
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp1074
-rw-r--r--libsolidity/codegen/ABIFunctions.h172
-rw-r--r--libsolidity/codegen/ArrayUtils.h2
-rw-r--r--libsolidity/codegen/Compiler.h6
-rw-r--r--libsolidity/codegen/CompilerContext.cpp5
-rw-r--r--libsolidity/codegen/CompilerContext.h15
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp35
-rw-r--r--libsolidity/codegen/CompilerUtils.h10
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp3
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp5
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
-rw-r--r--libsolidity/formal/SMTChecker.cpp588
-rw-r--r--libsolidity/formal/SMTChecker.h114
-rw-r--r--libsolidity/formal/SMTLib2Interface.cpp187
-rw-r--r--libsolidity/formal/SMTLib2Interface.h75
-rw-r--r--libsolidity/formal/SolverInterface.h178
-rw-r--r--libsolidity/formal/Z3Interface.cpp189
-rw-r--r--libsolidity/formal/Z3Interface.h65
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp4
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp32
-rw-r--r--libsolidity/inlineasm/AsmParser.h4
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp2
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h2
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScope.h2
-rw-r--r--libsolidity/interface/ABI.cpp7
-rw-r--r--libsolidity/interface/CompilerStack.cpp101
-rw-r--r--libsolidity/interface/CompilerStack.h26
-rw-r--r--libsolidity/interface/ErrorReporter.h6
-rw-r--r--libsolidity/interface/ReadFile.h8
-rw-r--r--libsolidity/interface/StandardCompiler.cpp12
-rw-r--r--libsolidity/interface/StandardCompiler.h4
-rw-r--r--libsolidity/parsing/Parser.cpp121
-rw-r--r--libsolidity/parsing/Parser.h6
-rw-r--r--libsolidity/parsing/ParserBase.cpp13
-rw-r--r--libsolidity/parsing/ParserBase.h22
-rw-r--r--libsolidity/parsing/Scanner.h10
-rw-r--r--libsolidity/parsing/Token.h7
-rw-r--r--lllc/CMakeLists.txt13
-rw-r--r--scripts/Dockerfile2
-rwxr-xr-xscripts/install_deps.sh55
-rwxr-xr-xscripts/release_ppa.sh16
-rwxr-xr-xscripts/test_emscripten.sh5
-rw-r--r--snap/snapcraft.yaml2
-rw-r--r--solc/CMakeLists.txt44
-rw-r--r--solc/CommandLineInterface.cpp52
-rw-r--r--solc/CommandLineInterface.h2
-rw-r--r--solc/jsonCompiler.cpp17
-rw-r--r--solc/main.cpp2
-rw-r--r--test/CMakeLists.txt29
-rw-r--r--test/Metadata.cpp1
-rw-r--r--test/RPCSession.h2
-rw-r--r--test/boostTest.cpp1
-rwxr-xr-xtest/externalTests.sh49
-rw-r--r--test/libdevcore/IndentedWriter.cpp75
-rw-r--r--test/libevmasm/Optimiser.cpp1
-rw-r--r--test/libjulia/Parser.cpp20
-rw-r--r--test/libsolidity/ABIEncoderTests.cpp405
-rw-r--r--test/libsolidity/ASTJSON.cpp2
-rw-r--r--test/libsolidity/InlineAssembly.cpp22
-rw-r--r--test/libsolidity/JSONCompiler.cpp2
-rw-r--r--test/libsolidity/Metadata.cpp30
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp167
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp115
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp546
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp4
-rw-r--r--test/libsolidity/SolidityParser.cpp87
-rw-r--r--test/libsolidity/SolidityTypes.cpp4
-rw-r--r--test/libsolidity/StandardCompiler.cpp2
137 files changed, 5297 insertions, 1886 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 60a8158d..87a141a9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,19 +8,20 @@ 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})
+option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
+
# Let's find our dependencies
include(EthDependencies)
include(deps/jsoncpp.cmake)
+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/Changelog.md b/Changelog.md
index 99466fd1..b7874206 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,33 @@
+### 0.4.16 (2017-08-24)
+
+Features:
+ * ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``,
+ ``nonpayable`` and ``payable``.
+ * 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,
+ ``constant`` remains an alias for ``view`` and pureness is not enforced yet,
+ so use with care.
+ * Static Analyzer: Warn about large storage structures.
+ * Syntax Checker: Support ``pragma experimental <feature>;`` to turn on
+ experimental features.
+ * Type Checker: More detailed error message for invalid overrides.
+ * Type Checker: Warn about shifting a literal.
+
+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``.
+ * Type Checker: Disallow constructors in libraries.
+ * Type Checker: Reject the creation of interface contracts using the ``new`` statement.
+
### 0.4.15 (2017-08-08)
Features:
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 00000000..fd506ba5
--- /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
diff --git a/cmake/CMakeParseArguments.cmake b/cmake/CMakeParseArguments.cmake
deleted file mode 100644
index 8553f38f..00000000
--- a/cmake/CMakeParseArguments.cmake
+++ /dev/null
@@ -1,161 +0,0 @@
-#.rst:
-# CMakeParseArguments
-# -------------------
-#
-#
-#
-# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords>
-# <multi_value_keywords> 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 <options> 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 <one_value_keywords> argument contains all keywords for this macro
-# which are followed by one value, like e.g. DESTINATION keyword of the
-# install() command.
-#
-# The <multi_value_keywords> 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 <options>, <one_value_keywords> and
-# <multi_value_keywords> a variable composed of the given <prefix>
-# followed by "_" and the name of the respective keyword. These
-# variables will then hold the respective value from the argument list.
-# For the <options> keywords this will be TRUE or FALSE.
-#
-# All remaining arguments are collected in a variable
-# <prefix>_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 <neundorf@kde.org>
-#
-# 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/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake
index 4ce9d22d..117dd319 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.
@@ -63,13 +75,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")
@@ -86,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)
@@ -103,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.
#
@@ -198,7 +170,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 +189,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 +227,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 a5e9b0c5..1204f186 100644
--- a/cmake/EthDependencies.cmake
+++ b/cmake/EthDependencies.cmake
@@ -43,62 +43,9 @@ find_program(CTEST_COMMAND ctest)
## use multithreaded boost libraries, with -mt suffix
set(Boost_USE_MULTITHREADED ON)
+option(Boost_USE_STATIC_LIBS "Link Boost statically" 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()
-
- 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)
+find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system)
eth_show_dependency(Boost boost)
@@ -108,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 746ba42e..00000000
--- 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="$<CONFIGURATION>"
- -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 68fd35d1..a473abcb 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 6bcf1e78..00000000
--- a/cmake/FindPackageHandleStandardArgs.cmake
+++ /dev/null
@@ -1,382 +0,0 @@
-#.rst:
-# FindPackageHandleStandardArgs
-# -----------------------------
-#
-#
-#
-# FIND_PACKAGE_HANDLE_STANDARD_ARGS(<name> ... )
-#
-# 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 <packagename>_FOUND variable. The
-# package is considered found if all variables <var1>... 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(<name>
-# (DEFAULT_MSG|"Custom failure message") <var1>...<varN> )
-#
-# If the variables <var1> to <varN> are all valid, then
-# <UPPERCASED_NAME>_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 <resultVar>]
-# [REQUIRED_VARS <var1>...<varN>]
-# [VERSION_VAR <versionvar>]
-# [HANDLE_COMPONENTS]
-# [CONFIG_MODE]
-# [FAIL_MESSAGE "Custom failure message"] )
-#
-# In this mode, the name of the result-variable can be set either to
-# either <UPPERCASED_NAME>_FOUND or <OriginalCase_Name>_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 <UPPERCASED_NAME>_FOUND.
-#
-# As in the simple mode, if <var1> through <varN> are all valid,
-# <packagename>_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 <packagename>_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 <NAME>_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 <var1>. 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)
- # <name>_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 <package>_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 a0349d3d..00000000
--- a/cmake/FindPackageMessage.cmake
+++ /dev/null
@@ -1,57 +0,0 @@
-#.rst:
-# FindPackageMessage
-# ------------------
-#
-#
-#
-# FIND_PACKAGE_MESSAGE(<name> "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 440e7d74..00000000
--- 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/FindZ3.cmake b/cmake/FindZ3.cmake
new file mode 100644
index 00000000..971d3b4b
--- /dev/null
+++ b/cmake/FindZ3.cmake
@@ -0,0 +1,7 @@
+find_path(Z3_INCLUDE_DIR z3++.h)
+find_library(Z3_LIBRARY NAMES z3 )
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
+
+# TODO: Create IMPORTED library for Z3.
+
diff --git a/cmake/UseDev.cmake b/cmake/UseDev.cmake
deleted file mode 100644
index 68df691a..00000000
--- 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 5080f71b..00000000
--- 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()
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 2cf57427..c0969cae 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
-- `fixed<M>x<N>`: 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)`.
+- `fixed<M>x<N>`: 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)`.
- `ufixed<M>x<N>`: unsigned variant of `fixed<M>x<N>`.
@@ -293,8 +293,9 @@ 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-functions>`);
-- `payable`: `true` if function accepts ether, defaults to `false`.
+- `constant`: `true` if function is :ref:`specified to not modify blockchain state <view-functions>`);
+- `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 <pure-functions>`), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
`type` can be omitted, defaulting to `"function"`.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 4e665b7e..6495699f 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -96,6 +96,31 @@ 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.
+ //
+ // 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) }
+ {
+ o_sum := add(o_sum, mload(data))
+ }
+ }
+ }
}
@@ -125,7 +150,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``.
@@ -545,6 +570,20 @@ The following example computes the sum of an area in memory.
}
}
+For loops can also be written so that they behave like while loops:
+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
---------
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 33f7bae9..ea242085 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",
diff --git a/docs/contracts.rst b/docs/contracts.rst
index da797702..50e7f3d1 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
@@ -461,29 +461,53 @@ 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) {
- return a * (b + 42);
+ function f(uint a, uint b) view returns (uint) {
+ return a * (b + 42) + now;
}
}
.. 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 ``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 constant method is not modifying state.
+ The compiler does not enforce yet that a ``pure`` method is not reading from the state.
.. index:: ! fallback function, function;fallback
@@ -626,7 +650,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("0x1234...ab67" /* address */);
var event = clientReceipt.Deposit();
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 73210991..5f1a981e 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -658,7 +658,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:`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.
diff --git a/docs/grammar.txt b/docs/grammar.txt
index 6c041460..041728c5 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 ';')* )? '}'
@@ -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 = 'pure' | 'constant' | 'view' | 'payable'
Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
@@ -142,9 +143,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* '}'
diff --git a/docs/index.rst b/docs/index.rst
index dea11a86..8c33fb9d 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 <https://gitter.im/ethereum/solidity/>`_.
Ideas for improving Solidity or this documentation are always welcome!
-See also `Russian version (русский перевод) <https://github.com/ethereum/wiki/wiki/%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_.
+See also `Russian version (русский перевод) <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_.
Contents
========
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index 35eb2d93..782bb606 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:
@@ -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
============================
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index dc7c6cc9..1a3cf638 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 <https://en.wikipedia.org/wiki/Pragma_once>`_). .
+source code (e.g. `pragma once <https://en.wikipedia.org/wiki/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
@@ -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<getter-functions>` created by the ``public`` keyword
is a bit more complex in this case. It roughly looks like the
following::
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index e364bee7..e78c4807 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -494,22 +494,24 @@ 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<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
-.. index:: modifiers, constant, anonymous, indexed
+.. index:: modifiers, pure, view, payable, constant, anonymous, indexed
Modifiers
=========
+- ``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: 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
=================
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index dde4495b..ca6b970c 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
@@ -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);
}
diff --git a/docs/types.rst b/docs/types.rst
index dd9c6269..fb88b006 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -83,6 +83,8 @@ Operators:
* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>``
+.. _members-of-addresses:
+
Members of Addresses
^^^^^^^^^^^^^^^^^^^^
@@ -184,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
@@ -321,7 +325,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
@@ -333,14 +337,15 @@ be passed via and returned from external function calls.
Function types are notated as follows::
- function (<parameter types>) {internal|external} [constant] [payable] [returns (<return types>)]
+ function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
In contrast to the parameter types, the return types cannot be empty - if the
function type should not return anything, the whole ``returns (<return types>)``
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
diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py
index ef55c6a2..a828146f 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),
diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt
index c4f886a6..a1c4c2d3 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_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)
diff --git a/libdevcore/Common.h b/libdevcore/Common.h
index c5b09a80..9d6dd408 100644
--- a/libdevcore/Common.h
+++ b/libdevcore/Common.h
@@ -44,7 +44,6 @@
#include <unordered_set>
#include <functional>
#include <string>
-#include <chrono>
#if defined(__GNUC__)
#pragma warning(push)
@@ -158,7 +157,7 @@ template <> inline u256 exp10<0>()
class ScopeGuard
{
public:
- ScopeGuard(std::function<void(void)> _f): m_f(_f) {}
+ explicit ScopeGuard(std::function<void(void)> _f): m_f(_f) {}
~ScopeGuard() { m_f(); }
private:
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index ab4bfe68..5df8986a 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -145,6 +145,24 @@ 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;
+ ret << std::hex << val;
+ return "0x" + ret.str();
+}
+
// Algorithms for string and string-like collections.
/// Escapes a string into the C-string representation.
@@ -177,7 +195,8 @@ template <class T>
inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const& _b)
{
std::vector<T> ret(_a);
- return ret += _b;
+ ret += _b;
+ return ret;
}
template <class T, class V>
diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h
index 5b1c7acf..141e9ffd 100644
--- a/libdevcore/FixedHash.h
+++ b/libdevcore/FixedHash.h
@@ -27,6 +27,7 @@
#include <cstdint>
#include <algorithm>
#include <boost/functional/hash.hpp>
+#include <boost/io/ios_state.hpp>
#include "CommonData.h"
namespace dev
@@ -59,7 +60,7 @@ public:
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 <unsigned M> explicit FixedHash(FixedHash<M> 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]; }
@@ -224,6 +225,7 @@ template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& va
template <unsigned N>
inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> 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/libdevcore/IndentedWriter.cpp b/libdevcore/IndentedWriter.cpp
new file mode 100644
index 00000000..96aaf0fa
--- /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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @date 2017
+ * Indented text writer.
+ */
+
+#include <libdevcore/IndentedWriter.h>
+#include <libdevcore/Assertions.h>
+
+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 00000000..4ddd87ed
--- /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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @date 2017
+ * Indented text writer.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <libdevcore/Exceptions.h>
+
+namespace dev
+{
+
+DEV_SIMPLE_EXCEPTION(IndentedWriterError);
+
+class IndentedWriter
+{
+public:
+ explicit IndentedWriter(): m_lines(std::vector<Line>{{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<Line> m_lines;
+};
+
+}
diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp
index 4bad8476..b0a4c755 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
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index 42b923df..0a3bf6b8 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/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp
index 76104866..419a8c0b 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 << " ???";
diff --git a/libevmasm/BlockDeduplicator.h b/libevmasm/BlockDeduplicator.h
index 797c2476..5640984b 100644
--- a/libevmasm/BlockDeduplicator.h
+++ b/libevmasm/BlockDeduplicator.h
@@ -45,7 +45,7 @@ using AssemblyItems = std::vector<AssemblyItem>;
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/CMakeLists.txt b/libevmasm/CMakeLists.txt
index 9cc3e93e..5c945c7d 100644
--- a/libevmasm/CMakeLists.txt
+++ b/libevmasm/CMakeLists.txt
@@ -1,14 +1,5 @@
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
+file(GLOB sources "*.cpp")
+file(GLOB headers "*.h")
-aux_source_directory(. SRC_LIST)
-
-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)
diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp
index 70324e7f..293cb02c 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 83fc9732..0b957a0e 100644
--- a/libevmasm/CommonSubexpressionEliminator.h
+++ b/libevmasm/CommonSubexpressionEliminator.h
@@ -61,7 +61,7 @@ public:
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.
@@ -147,7 +147,7 @@ private:
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<Id, Id> m_neededBy;
/// Current content of the stack.
diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp
index 2ecbfa7f..2efd2dc9 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();
@@ -159,7 +159,7 @@ AssemblyItems CodeCopyMethod::execute(Assembly& _assembly)
return actualCopyRoutine;
}
-AssemblyItems const& CodeCopyMethod::copyRoutine() const
+AssemblyItems const& CodeCopyMethod::copyRoutine()
{
AssemblyItems static copyRoutine{
u256(0),
@@ -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 85bdabac..82982e25 100644
--- a/libevmasm/ConstantOptimiser.h
+++ b/libevmasm/ConstantOptimiser.h
@@ -63,11 +63,11 @@ public:
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<size_t>(1, dev::bytesRequired(m_value)); }
@@ -84,7 +84,7 @@ protected:
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,11 +117,11 @@ 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;
+ static AssemblyItems const& copyRoutine();
};
/**
@@ -141,8 +141,8 @@ public:
);
}
- 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 @@ protected:
/// 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);
+ 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.
size_t m_maxSteps = 10000;
diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h
index 0a0fe5d0..4826eac2 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<KnownState> const& _state);
diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt
index db90025a..4cdc073a 100644
--- a/liblll/CMakeLists.txt
+++ b/liblll/CMakeLists.txt
@@ -1,18 +1,5 @@
-cmake_policy(SET CMP0015 NEW)
-set(CMAKE_AUTOMOC OFF)
+file(GLOB sources "*.cpp")
+file(GLOB headers "*.h")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
-
-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 devcore)
diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h
index 6622de3e..95d21563 100644
--- a/liblll/CodeFragment.h
+++ b/liblll/CodeFragment.h
@@ -50,8 +50,8 @@ public:
private:
void finalise(CompilerState const& _cs);
- template <class T> void error() const { BOOST_THROW_EXCEPTION(T() ); }
- template <class T> void error(std::string const& reason) const {
+ template <class T> static void error() { BOOST_THROW_EXCEPTION(T() ); }
+ template <class T> static void error(std::string const& reason) {
auto err = T();
err << errinfo_comment(reason);
BOOST_THROW_EXCEPTION(err);
diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp
index 9701e16b..d53dec7e 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 c29d3b7d..96a0246d 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/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 2342f0f9..f7c1a390 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -1,23 +1,20 @@
-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)
-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})
+file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp")
+file(GLOB_RECURSE headers "*.h" "../libjulia/*.h")
-eth_use(${EXECUTABLE} REQUIRED Dev::soldevcore Solidity::solevmasm)
+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()
-install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
+add_library(solidity ${sources} ${headers})
+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/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 46477e1e..2f130414 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;
}
@@ -92,6 +94,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<StructDefinition const*> 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 +173,34 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly)
return true;
}
+
+bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set<StructDefinition const*>& _structsSeen)
+{
+ switch (_type.category())
+ {
+ case Type::Category::Array:
+ {
+ auto const& t = dynamic_cast<ArrayType const&>(_type);
+ return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length());
+ }
+ case Type::Category::Struct:
+ {
+ auto const& t = dynamic_cast<StructType const&>(_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<MappingType const&>(_type).valueType(), _structsSeen);
+ }
+ default:
+ break;
+ }
+ return bigint(1);
+}
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 21a487df..a3080b42 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -65,6 +65,9 @@ private:
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<StructDefinition const*>& _structsSeen);
+
ErrorReporter& m_errorReporter;
/// Flag that indicates whether the current contract definition is a library.
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index bde0e616..d2571cd3 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -18,6 +18,7 @@
#include <libsolidity/analysis/SyntaxChecker.h>
#include <memory>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
@@ -33,9 +34,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 +59,46 @@ 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<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
+ if (literals.size() == 0)
+ m_errorReporter.syntaxError(
+ _pragma.location(),
+ "Experimental feature name is missing."
+ );
+ else if (literals.size() > 1)
+ m_errorReporter.syntaxError(
+ _pragma.location(),
+ "Stray arguments."
+ );
+ else
+ {
+ string const literal = literals[0];
+ if (literal.empty())
+ m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
+ else if (!ExperimentalFeatureNames.count(literal))
+ m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
+ 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(ExperimentalFeatureNames.at(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")
{
vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
@@ -81,6 +114,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 fb5cc6d7..fa34bab3 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -77,6 +77,8 @@ private:
bool m_versionPragmaFound = false;
int m_inLoopDepth = 0;
+
+ SourceUnit const* m_sourceUnit = nullptr;
};
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 6852c13d..99f3c64c 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())
@@ -277,21 +287,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
- )
- m_errorReporter.typeError(overriding->location(), "Override changes extended function signature.");
- }
+ checkFunctionOverride(*overriding, *function);
+
functions[name].push_back(function);
}
for (ModifierDefinition const* modifier: contract->functionModifiers())
@@ -308,6 +307,41 @@ 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())
+ overrideError(function, super, "Overriding function visibility differs.");
+
+ 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.");
+}
+
+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)
{
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
@@ -396,7 +430,11 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from.");
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(
@@ -429,7 +467,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)
@@ -475,8 +513,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<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
@@ -514,6 +550,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())
@@ -1282,8 +1321,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
@@ -1297,7 +1337,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."
);
@@ -1518,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(),
@@ -1949,4 +1991,3 @@ void TypeChecker::requireLValue(Expression const& _expression)
else if (!_expression.annotation().isLValue)
m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue.");
}
-
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ee43d13a..f2e13765 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -62,6 +62,9 @@ private:
/// 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 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/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 1d68231e..7f4dea0e 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST_accept.h>
#include <libdevcore/SHA3.h>
@@ -188,7 +189,6 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
{
if (!m_interfaceFunctionList)
{
- set<string> functionsSeen;
set<string> signaturesSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
@@ -371,6 +371,15 @@ string FunctionDefinition::externalSignature() const
return FunctionType(*this).externalSignature();
}
+string FunctionDefinition::fullyQualifiedName() const
+{
+ auto const* contract = dynamic_cast<ContractDefinition const*>(scope());
+ solAssert(contract, "Enclosing scope of function definition was not set.");
+
+ auto fname = name().empty() ? "<fallback>" : 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 3e97286b..4592a190 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -28,6 +28,7 @@
#include <libsolidity/ast/Types.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTEnums.h>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
@@ -152,6 +153,24 @@ public:
/// 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<ASTString> const& _name,
@@ -566,21 +585,19 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility,
+ StateMutability _stateMutability,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
- bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
- bool _isPayable,
ASTPointer<Block> 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)
{}
@@ -588,13 +605,14 @@ 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 isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> 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();
@@ -615,9 +633,8 @@ public:
virtual FunctionDefinitionAnnotation& annotation() const override;
private:
+ StateMutability m_stateMutability;
bool m_isConstructor;
- bool m_isDeclaredConst;
- bool m_isPayable;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;
};
@@ -819,11 +836,10 @@ private:
*/
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;
};
@@ -877,11 +893,10 @@ public:
ASTPointer<ParameterList> const& _parameterTypes,
ASTPointer<ParameterList> 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;
@@ -895,15 +910,14 @@ public:
{
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 isPayable() const { return m_stateMutability == StateMutability::Payable; }
private:
ASTPointer<ParameterList> m_parameterTypes;
ASTPointer<ParameterList> m_returnTypes;
Declaration::Visibility m_visibility;
- bool m_isDeclaredConst;
- bool m_isPayable;
+ StateMutability m_stateMutability;
};
/**
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index f757f03c..fd9efb4d 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -23,6 +23,7 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ExperimentalFeatures.h>
#include <map>
#include <memory>
@@ -61,6 +62,8 @@ struct SourceUnitAnnotation: ASTAnnotation
std::string path;
/// The exported symbols (all global symbols).
std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
+ /// Experimental features.
+ std::set<ExperimentalFeature> experimentalFeatures;
};
struct ImportAnnotation: ASTAnnotation
diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h
new file mode 100644
index 00000000..5ba21907
--- /dev/null
+++ b/libsolidity/ast/ASTEnums.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @date 2017
+ * Enums for AST classes.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <string>
+
+namespace dev
+{
+namespace solidity
+{
+
+// How a function can mutate the EVM state.
+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:
+ 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 cfeeaa58..15735368 100644
--- a/libsolidity/ast/ASTForward.h
+++ b/libsolidity/ast/ASTForward.h
@@ -95,6 +95,5 @@ using ASTPointer = std::shared_ptr<T>;
using ASTString = std::string;
-
}
}
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index e4a602cb..afc53bfe 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -15,8 +15,7 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
- * @author Lefteris <lefteris@ethdev.com>
- * @date 2015
+ * @date 2017
* Converts the AST into json format
*/
@@ -81,28 +80,30 @@ 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));
+ {
+ 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())
- m_currentValue["children"].append(std::move(child));
+ {
+ if (!m_currentValue["children"].isArray())
+ m_currentValue["children"] = Json::arrayValue;
+ appendMove(m_currentValue["children"], std::move(child));
+ }
}
else
{
@@ -147,7 +148,7 @@ Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr<std::vector<Type
{
Json::Value arguments(Json::arrayValue);
for (auto const& tp: *_tps)
- arguments.append(typePointerToJson(tp));
+ appendMove(arguments, typePointerToJson(tp));
return arguments;
}
else
@@ -186,7 +187,7 @@ void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node)
_stream << toJson(_node);
}
-Json::Value ASTJsonConverter::toJson(ASTNode const& _node)
+Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node)
{
_node.accept(*this);
return std::move(m_currentValue);
@@ -285,7 +286,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()))
@@ -323,9 +324,11 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
- make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
+ // FIXME: remove with next breaking release
+ make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
make_pair("payable", _node.isPayable()),
- make_pair("visibility", visibility(_node.visibility())),
+ make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
+ 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 +349,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 +364,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()))
});
@@ -377,12 +380,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;
@@ -418,8 +415,10 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
{
setJsonNode(_node, "FunctionTypeName", {
make_pair("payable", _node.isPayable()),
- make_pair("visibility", visibility(_node.visibility())),
- make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
+ 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())),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
@@ -545,7 +544,7 @@ bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
Json::Value varDecs(Json::arrayValue);
for (auto const& v: _node.annotation().assignments)
- varDecs.append(idOrNull(v));
+ appendMove(varDecs, idOrNull(v));
setJsonNode(_node, "VariableDeclarationStatement", {
make_pair("assignments", std::move(varDecs)),
make_pair("declarations", toJson(_node.declarations())),
@@ -730,23 +729,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 27114c2a..60c660c1 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -49,13 +49,16 @@ public:
);
/// Output the json representation of the AST to _stream.
void print(std::ostream& _stream, ASTNode const& _node);
- Json::Value toJson(ASTNode const& _node);
+ Json::Value&& toJson(ASTNode const& _node);
template <class T>
Json::Value toJson(std::vector<ASTPointer<T>> 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;
@@ -73,7 +76,6 @@ public:
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;
@@ -119,7 +121,7 @@ private:
);
std::string sourceLocationToString(SourceLocation const& _location) const;
std::string namePathToString(std::vector<ASTString> const& _namePath) const;
- Json::Value idOrNull(ASTNode const* _pt)
+ static Json::Value idOrNull(ASTNode const* _pt)
{
return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue;
}
@@ -128,19 +130,18 @@ private:
return _node ? toJson(*_node) : Json::nullValue;
}
Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _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);
std::string literalTokenKind(Token::Value _token);
std::string type(Expression const& _expression);
std::string type(VariableDeclaration const& _varDecl);
- int nodeId(ASTNode const& _node)
+ static int nodeId(ASTNode const& _node)
{
return _node.id();
}
template<class Container>
- Json::Value getContainerIds(Container const& container)
+ static Json::Value getContainerIds(Container const& container)
{
Json::Value tmp(Json::arrayValue);
for (auto const& element: container)
@@ -156,6 +157,12 @@ private:
std::vector<std::pair<std::string, Json::Value>> &_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/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index 23eb3b64..392179ef 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();
}
@@ -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 4a37f17f..d6897dfd 100644
--- a/libsolidity/ast/ASTPrinter.h
+++ b/libsolidity/ast/ASTPrinter.h
@@ -60,7 +60,6 @@ public:
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 @@ public:
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;
@@ -146,7 +144,7 @@ private:
std::string m_source;
ASTNode const* m_ast;
GasEstimator::ASTGasConsumption m_gasCosts;
- std::ostream* m_ostream;
+ std::ostream* m_ostream = nullptr;
};
}
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index 20be634b..b726d592 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -58,7 +58,6 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 7c1c64b0..904d9ff6 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);
diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h
new file mode 100644
index 00000000..2c089671
--- /dev/null
+++ b/libsolidity/ast/ExperimentalFeatures.h
@@ -0,0 +1,53 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * List of experimental features.
+ */
+
+#pragma once
+
+#include <map>
+
+namespace dev
+{
+namespace solidity
+{
+
+enum class ExperimentalFeature
+{
+ SMTChecker,
+ ABIEncoderV2, // new ABI encoder that makes use of JULIA
+ Test,
+ TestOnlyAnalysis
+};
+
+static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis =
+{
+ { ExperimentalFeature::SMTChecker, true },
+ { ExperimentalFeature::TestOnlyAnalysis, true },
+};
+
+static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
+{
+ { "SMTChecker", ExperimentalFeature::SMTChecker },
+ { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
+ { "__test", ExperimentalFeature::Test },
+ { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
+};
+
+}
+}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 56fdd508..5e61cdee 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<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, false, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)},
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
@@ -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<TupleType>();
- // 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
@@ -738,18 +739,18 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() == Category::Integer)
{
- auto targetType = dynamic_cast<IntegerType const*>(&_convertTo);
if (m_value == rational(0))
return true;
if (isFractional())
return false;
- int forSignBit = (targetType->isSigned() ? 1 : 0);
+ IntegerType const& targetType = dynamic_cast<IntegerType const&>(_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;
}
@@ -1408,6 +1409,11 @@ unsigned ArrayType::calldataEncodedSize(bool _padded) const
return unsigned(size);
}
+bool ArrayType::isDynamicallyEncoded() const
+{
+ return isDynamicallySized() || baseType()->isDynamicallyEncoded();
+}
+
u256 ArrayType::storageSize() const
{
if (isDynamicallySized())
@@ -1523,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<ArrayType>(DataLocation::Memory, baseExt);
@@ -1710,6 +1714,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;
@@ -1989,8 +1998,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;
@@ -1998,6 +2006,9 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
TypePointers retParams;
vector<string> retParamNames;
+ if (_isInternal && m_stateMutability == StateMutability::Payable)
+ m_stateMutability = StateMutability::NonPayable;
+
params.reserve(_function.parameters().size());
paramNames.reserve(_function.parameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
@@ -2019,7 +2030,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<string> paramNames;
@@ -2079,7 +2090,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<string> paramNames;
@@ -2096,14 +2107,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.");
@@ -2131,7 +2138,9 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
FunctionDefinition const* constructor = _contract.constructor();
TypePointers parameters;
strings parameterNames;
- bool payable = false;
+ StateMutability stateMutability = StateMutability::NonPayable;
+
+ solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, "");
if (constructor)
{
@@ -2140,8 +2149,10 @@ 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<FunctionType>(
parameters,
TypePointers{make_shared<ContractType>(_contract)},
@@ -2150,8 +2161,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
Kind::Creation,
false,
nullptr,
- false,
- payable
+ stateMutability
);
}
@@ -2208,8 +2218,7 @@ string FunctionType::identifier() const
case Kind::Require: id += "require";break;
default: solAssert(false, "Unknown function location."); break;
}
- if (isConstant())
- id += "_constant";
+ id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
if (m_gasSet)
id += "gas";
@@ -2224,23 +2233,21 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
- FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (m_kind != other.m_kind)
- return false;
- if (m_isConstant != other.isConstant())
+ FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
+ if (
+ m_kind != other.m_kind ||
+ m_stateMutability != other.stateMutability() ||
+ 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)
@@ -2292,10 +2299,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 (m_isConstant)
- name += " constant";
- if (m_isPayable)
- name += " payable";
+ if (m_stateMutability != StateMutability::NonPayable)
+ name += " " + stateMutabilityToString(m_stateMutability);
if (m_kind == Kind::External)
name += " external";
if (!m_returnParameterTypes.empty())
@@ -2344,14 +2349,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)
@@ -2389,10 +2406,14 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
return FunctionTypePointer();
return make_shared<FunctionType>(
- 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_stateMutability
);
}
@@ -2409,7 +2430,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<FunctionType>(
@@ -2420,8 +2441,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetValue,
false,
nullptr,
- false,
- false,
+ StateMutability::NonPayable,
m_gasSet,
m_valueSet
)
@@ -2438,8 +2458,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetGas,
false,
nullptr,
- false,
- false,
+ StateMutability::NonPayable,
m_gasSet,
m_valueSet
)
@@ -2575,8 +2594,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_kind,
m_arbitraryParameters,
m_declaration,
- m_isConstant,
- m_isPayable,
+ m_stateMutability,
m_gasSet || _setGas,
m_valueSet || _setValue,
m_bound
@@ -2625,8 +2643,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
kind,
m_arbitraryParameters,
m_declaration,
- m_isConstant,
- m_isPayable,
+ m_stateMutability,
m_gasSet,
m_valueSet,
_bound
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 5d2bdca0..ce2d3bf8 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -24,6 +24,7 @@
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ASTEnums.h>
#include <libsolidity/parsing/Token.h>
#include <libdevcore/Common.h>
@@ -187,6 +188,7 @@ public:
/// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type,
/// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded
/// in calldata.
+ /// @note: This should actually not be called on types, where isDynamicallyEncoded returns true.
/// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes.
virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; }
/// @returns the size of this data type in bytes when stored in memory. For memory-reference
@@ -194,8 +196,10 @@ public:
virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); }
/// Convenience version of @see calldataEncodedSize(bool)
unsigned calldataEncodedSize() const { return calldataEncodedSize(true); }
- /// @returns true if the type is dynamically encoded in calldata
+ /// @returns true if the type is a dynamic array
virtual bool isDynamicallySized() const { return false; }
+ /// @returns true if the type is dynamically encoded in the ABI
+ virtual bool isDynamicallyEncoded() const { return false; }
/// @returns the number of storage slots required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 storageSize() const { return 1; }
@@ -609,6 +613,7 @@ public:
virtual bool operator==(const Type& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
+ virtual bool isDynamicallyEncoded() const override;
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
virtual unsigned sizeOnStack() const override;
@@ -723,6 +728,7 @@ public:
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; }
@@ -884,8 +890,7 @@ public:
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),
@@ -894,8 +899,7 @@ public:
_kind,
_arbitraryParameters,
nullptr,
- _constant,
- _payable
+ _stateMutability
)
{
}
@@ -912,8 +916,7 @@ public:
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
@@ -923,12 +926,11 @@ public:
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_isConstant(_isConstant),
- m_isPayable(_isPayable),
m_declaration(_declaration)
{
solAssert(
@@ -980,6 +982,7 @@ public:
/// @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).
@@ -990,12 +993,11 @@ public:
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
- bool isConstant() const { return m_isConstant; }
/// @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<ASTString> documentation() const;
@@ -1028,13 +1030,12 @@ private:
std::vector<std::string> m_parameterNames;
std::vector<std::string> 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/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
new file mode 100644
index 00000000..a2938ed7
--- /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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <chris@ethereum.org>
+ * @date 2017
+ * Routines that generate JULIA code related to ABI encoding, decoding and type conversions.
+ */
+
+#include <libsolidity/codegen/ABIFunctions.h>
+
+#include <libdevcore/Whiskers.h>
+
+#include <libsolidity/ast/AST.h>
+
+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, <headSize>)
+ <encodeElements>
+ <deepestStackElement> := 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, <pos>), sub(tail, $headStart))
+ tail := <abiEncode>(<values> tail)
+ )") :
+ string(R"(
+ <abiEncode>(<values> add($headStart, <pos>))
+ )")
+ );
+ 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 <functionName>(value) -> cleaned {
+ <body>
+ }
+ )");
+ templ("functionName", functionName);
+ switch (_type.category())
+ {
+ case Type::Category::Integer:
+ {
+ IntegerType const& type = dynamic_cast<IntegerType const&>(_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<FixedBytesType const&>(_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<EnumType const&>(_type).numberOfMembers();
+ solAssert(members > 0, "empty enum should have caused a parser error.");
+ Whiskers w("switch lt(value, <members>) case 0 { <failure> } 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 <functionName>(value) -> converted {
+ <body>
+ }
+ )");
+ 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<RationalNumberType const*>(&_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<FixedBytesType const&>(_to);
+ body =
+ Whiskers("converted := <shiftLeft>(<clean>(value))")
+ ("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8))
+ ("clean", cleanupFunction(_from))
+ .render();
+ }
+ else if (toCategory == Type::Category::Enum)
+ {
+ solAssert(_from.mobileType(), "");
+ body =
+ Whiskers("converted := <cleanEnum>(<cleanInt>(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<IntegerType const&>(_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<IntegerType const&>(_from) :
+ addressType;
+ if (to.numBits() > from.numBits())
+ cleanupType = &from;
+ }
+ body =
+ Whiskers("converted := <cleanInt>(value)")
+ ("cleanInt", cleanupFunction(*cleanupType))
+ .render();
+ }
+ break;
+ }
+ case Type::Category::Bool:
+ {
+ solAssert(_from == _to, "Invalid conversion for bool.");
+ body =
+ Whiskers("converted := <clean>(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<FixedBytesType const&>(_from);
+ if (toCategory == Type::Category::Integer)
+ body =
+ Whiskers("converted := <convert>(<shift>(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 := <clean>(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<decltype(enumType)>(_from);
+ body =
+ Whiskers("converted := <clean>(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 <functionName>(addr_and_selector) -> cleaned {
+ cleaned := <clean>(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 <functionName>(addr, selector) -> combined {
+ combined := <shl64>(or(<shl32>(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<ArrayType const*>(&to))
+ {
+ solAssert(_from.category() == Type::Category::Array, "");
+ solAssert(to.dataStoredIn(DataLocation::Memory), "");
+ ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_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<StructType const*>(&to))
+ {
+ solUnimplementedAssert(false, "Structs not yet implemented.");
+ }
+ else if (_from.category() == Type::Category::Function)
+ return abiEncodingFunctionFunctionType(
+ dynamic_cast<FunctionType const&>(_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 <functionName>(value, pos) {
+ mstore(pos, <cleanupConvert>)
+ }
+ )");
+ 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<ArrayType const&>(_from);
+ auto const& toArrayType = dynamic_cast<ArrayType const&>(_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 <functionName>(start, length, pos) -> end {
+ <storeLength> // might update pos
+ <copyFun>(start, pos, length)
+ end := add(pos, <roundUpFun>(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 <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let headStart := pos
+ let tail := add(pos, mul(length, 0x20))
+ let srcPtr := <dataAreaFun>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, 1) }
+ {
+ mstore(pos, sub(tail, headStart))
+ tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
+ srcPtr := <nextArrayElement>(srcPtr)
+ pos := add(pos, <elementEncodedSize>)
+ }
+ pos := tail
+ <assignEnd>
+ }
+ )" :
+ R"(
+ function <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let srcPtr := <dataAreaFun>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, 1) }
+ {
+ <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
+ srcPtr := <nextArrayElement>(srcPtr)
+ pos := add(pos, <elementEncodedSize>)
+ }
+ <assignEnd>
+ }
+ )"
+ );
+ 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 <functionName>(value, pos) -> end {
+ let length := <lengthFun>(value)
+ mstore(pos, length)
+ <copyFun>(add(value, 0x20), add(pos, 0x20), length)
+ end := add(add(pos, 0x20), <roundUpFun>(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 <functionName>(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 := <arrayDataSlot>(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 <functionName>(value, pos) <return> {
+ let length := <lengthFun>(value)
+ <storeLength> // might update pos
+ let originalPos := pos
+ let srcPtr := <dataArea>(value)
+ for { let i := 0 } lt(i, length) { i := add(i, <itemsPerSlot>) }
+ {
+ let data := sload(srcPtr)
+ <#items>
+ <encodeToMemoryFun>(<shiftRightFun>(data), pos)
+ pos := add(pos, <elementEncodedSize>)
+ </items>
+ srcPtr := add(srcPtr, 1)
+ }
+ pos := add(originalPos, mul(length, <elementEncodedSize>))
+ <assignEnd>
+ }
+ )"
+ );
+ 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<std::map<std::string, std::string>> 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<StringLiteralType const&>(_from);
+ string const& value = strType.value();
+ solAssert(_from.sizeOnStack() == 0, "");
+
+ if (_to.isDynamicallySized())
+ {
+ Whiskers templ(R"(
+ function <functionName>(pos) -> end {
+ mstore(pos, <length>)
+ <#word>
+ mstore(add(pos, <offset>), <wordValue>)
+ </word>
+ end := add(pos, <overallSize>)
+ }
+ )");
+ 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<map<string, string>> 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 <functionName>(pos) {
+ mstore(pos, <wordValue>)
+ }
+ )");
+ 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 <functionName>(addr_and_function_id, pos) {
+ mstore(pos, <cleanExtFun>(addr_and_function_id))
+ }
+ )")
+ ("functionName", functionName)
+ ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction())
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return Whiskers(R"(
+ function <functionName>(addr, function_id, pos) {
+ mstore(pos, <combineExtFun>(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 <functionName>(src, dst, length) {
+ calldatacopy(dst, src, length)
+ // clear end
+ mstore(add(dst, length), 0)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ }
+ else
+ {
+ return Whiskers(R"(
+ function <functionName>(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 <functionName>(value) -> newValue {
+ newValue := mul(value, <multiplier>)
+ })")
+ ("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 <functionName>(value) -> newValue {
+ newValue := <div>(value, <multiplier>)
+ })")
+ ("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 <functionName>(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 <functionName>(value) -> length {
+ <body>
+ }
+ )");
+ 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 <functionName>(memPtr) -> dataPtr {
+ dataPtr := add(memPtr, 0x20)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else
+ return Whiskers(R"(
+ function <functionName>(memPtr) -> dataPtr {
+ dataPtr := memPtr
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ }
+ else if (_type.dataStoredIn(DataLocation::Storage))
+ {
+ if (_type.isDynamicallySized())
+ {
+ Whiskers w(R"(
+ function <functionName>(slot) -> dataSlot {
+ mstore(0, slot)
+ dataSlot := keccak256(0, 0x20)
+ }
+ )");
+ w("functionName", functionName);
+ return w.render();
+ }
+ else
+ {
+ Whiskers w(R"(
+ function <functionName>(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 <functionName>(memPtr) -> nextPtr {
+ nextPtr := add(memPtr, 0x20)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else if (_type.location() == DataLocation::Storage)
+ return Whiskers(R"(
+ function <functionName>(slot) -> nextSlot {
+ nextSlot := add(slot, 1)
+ }
+ )")
+ ("functionName", functionName)
+ .render();
+ else
+ solAssert(false, "");
+ });
+}
+
+string ABIFunctions::createFunction(string const& _name, function<string ()> 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 00000000..e43e2323
--- /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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <chris@ethereum.org>
+ * @date 2017
+ * Routines that generate JULIA code related to ABI encoding, decoding and type conversions.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTForward.h>
+
+#include <vector>
+#include <functional>
+#include <map>
+
+namespace dev {
+namespace solidity {
+
+class Type;
+class ArrayType;
+class StructType;
+class FunctionType;
+using TypePointer = std::shared_ptr<Type const>;
+using TypePointers = std::vector<TypePointer>;
+
+///
+/// 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<std::string()> const& _creator);
+
+ /// @returns the size of the static part of the encoding of the given types.
+ static size_t headSize(TypePointers const& _targetTypes);
+
+ /// Map from function name to code for a multi-use function.
+ std::map<std::string, std::string> m_requestedFunctions;
+};
+
+}
+}
diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h
index 806fbea7..f3ddc4ee 100644
--- a/libsolidity/codegen/ArrayUtils.h
+++ b/libsolidity/codegen/ArrayUtils.h
@@ -40,7 +40,7 @@ using TypePointer = std::shared_ptr<Type const>;
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/Compiler.h b/libsolidity/codegen/Compiler.h
index eef078c1..8c63ea9c 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -51,9 +51,9 @@ public:
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> 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.cpp b/libsolidity/codegen/CompilerContext.cpp
index bc4de3ee..ed780d0b 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 13821f67..96cbf6c1 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<eth::Assembly>()),
m_runtimeContext(_runtimeContext)
{
@@ -56,7 +56,9 @@ public:
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
}
- void addMagicGlobal(MagicVariableDeclaration const& _declaration);
+ void setExperimentalFeatures(std::set<ExperimentalFeature> 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);
void removeVariable(VariableDeclaration const& _declaration);
@@ -68,7 +70,6 @@ public:
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; }
@@ -207,8 +208,8 @@ public:
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
@@ -265,8 +266,8 @@ private:
} m_functionCompilationQueue;
eth::AssemblyPointer m_asm;
- /// Magic global variables like msg, tx or this, distinguished by type.
- std::set<Declaration const*> m_magicGlobals;
+ /// Activated experimental features.
+ std::set<ExperimentalFeature> m_experimentalFeatures;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
/// Storage offsets of state variables
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 782aad9d..a0fc5d55 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -25,6 +25,7 @@
#include <libevmasm/Instruction.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
+#include <libsolidity/codegen/ABIFunctions.h>
using namespace std;
@@ -182,6 +183,18 @@ void CompilerUtils::encodeToMemory(
if (_givenTypes.empty())
return;
+ else if (
+ _padToWordBoundaries &&
+ !_copyDynamicDataInPlace &&
+ m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
+ )
+ {
+ // 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:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
@@ -289,6 +302,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<string> 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 fb169463..18b70250 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();
@@ -103,6 +103,14 @@ public:
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: <length> <memptr>
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index fd0998d4..e53f1b94 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:
@@ -100,6 +100,7 @@ void ContractCompiler::initializeContext(
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
)
{
+ m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
m_context.setCompiledContracts(_compiledContracts);
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 521d485f..639bfc32 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
),
@@ -1812,7 +1811,7 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
setLValue<StorageItem>(_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 3b8cf1c6..5f6c3d64 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -119,7 +119,7 @@ private:
/// @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/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
new file mode 100644
index 00000000..fd78e578
--- /dev/null
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -0,0 +1,588 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/formal/SMTChecker.h>
+
+#ifdef HAVE_Z3
+#include <libsolidity/formal/Z3Interface.h>
+#else
+#include <libsolidity/formal/SMTLib2Interface.h>
+#endif
+
+#include <libsolidity/interface/ErrorReporter.h>
+
+#include <boost/range/adaptor/map.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback):
+#ifdef HAVE_Z3
+ m_interface(make_shared<smt::Z3Interface>()),
+#else
+ m_interface(make_shared<smt::SMTLib2Interface>(_readFileCallback)),
+#endif
+ m_errorReporter(_errorReporter)
+{
+ (void)_readFileCallback;
+}
+
+void SMTChecker::analyze(SourceUnit const& _source)
+{
+ if (_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
+ {
+ m_interface->reset();
+ m_currentSequenceCounter.clear();
+ m_nextFreeSequenceCounter.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.
+ // 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)
+ m_errorReporter.warning(
+ _varDecl.location(),
+ "Assertion checker does not yet support such variable declarations."
+ );
+ 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(),
+ "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<Identifier const*>(&_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<FunctionType const&>(*_funCall.expression().annotation().type);
+
+ std::vector<ASTPointer<Expression const>> 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<IntegerType const*>(_identifier.annotation().type.get()))
+ {
+ m_interface->addAssertion(expr(_identifier) == currentValue(*decl));
+ return;
+ }
+ else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_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<RationalNumberType const*>(&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<IntegerType const&>(*_op.annotation().commonType);
+ checkCondition(
+ value < minValue(intType),
+ _op.location(),
+ "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")",
+ "value",
+ &value
+ );
+ checkCondition(
+ value > maxValue(intType),
+ _op.location(),
+ "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")",
+ "value",
+ &value
+ );
+
+ 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<smt::Expression> expressionsToEvaluate;
+ vector<string> 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<string> values;
+ 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::SATISFIABLE:
+ {
+ std::ostringstream message;
+ message << _description << " happens here";
+ if (m_currentFunction)
+ {
+ message << " for:\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 << ".";
+ m_errorReporter.warning(_location, message.str());
+ break;
+ }
+ case smt::CheckResult::UNSATISFIABLE:
+ 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 (dynamic_cast<IntegerType const*>(_varDecl.type().get()))
+ {
+ solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, "");
+ solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, "");
+ solAssert(m_Variables.count(&_varDecl) == 0, "");
+ m_currentSequenceCounter[&_varDecl] = 0;
+ m_nextFreeSequenceCounter[&_varDecl] = 1;
+ m_Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int));
+ setValue(_varDecl, _setToZero);
+ }
+ 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 valueAtSequence(_decl, m_currentSequenceCounter.at(&_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), "");
+ 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<IntegerType const&>(*_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());
+}
+
+smt::Expression SMTChecker::maxValue(IntegerType const& _t)
+{
+ return smt::Expression(_t.maxValue());
+}
+
+smt::Expression SMTChecker::expr(Expression const& _e)
+{
+ if (!m_Expressions.count(&_e))
+ {
+ solAssert(_e.annotation().type, "");
+ switch (_e.annotation().type->category())
+ {
+ case Type::Category::RationalNumber:
+ {
+ if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(_e.annotation().type.get()))
+ solAssert(!rational->isFractional(), "");
+ m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
+ break;
+ }
+ case Type::Category::Integer:
+ m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
+ break;
+ case Type::Category::Bool:
+ m_Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e)));
+ break;
+ default:
+ solAssert(false, "Type not implemented.");
+ }
+ }
+ return m_Expressions.at(&_e);
+}
+
+smt::Expression SMTChecker::var(Declaration const& _decl)
+{
+ solAssert(m_Variables.count(&_decl), "");
+ return m_Variables.at(&_decl);
+}
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
new file mode 100644
index 00000000..d23fd201
--- /dev/null
+++ b/libsolidity/formal/SMTChecker.h
@@ -0,0 +1,114 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/formal/SolverInterface.h>
+#include <libsolidity/interface/ReadFile.h>
+
+#include <map>
+#include <string>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ErrorReporter;
+
+class SMTChecker: private ASTConstVisitor
+{
+public:
+ SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readCallback);
+
+ 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 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;
+ 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);
+
+ 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
+ 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);
+
+ 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.
+ 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);
+
+ std::shared_ptr<smt::SolverInterface> m_interface;
+ std::map<Declaration const*, int> m_currentSequenceCounter;
+ std::map<Declaration const*, int> m_nextFreeSequenceCounter;
+ std::map<Expression const*, smt::Expression> m_Expressions;
+ std::map<Declaration const*, smt::Expression> m_Variables;
+ 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 00000000..cbd766fb
--- /dev/null
+++ b/libsolidity/formal/SMTLib2Interface.cpp
@@ -0,0 +1,187 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/formal/SMTLib2Interface.h>
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ReadFile.h>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <array>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+using namespace dev::solidity::smt;
+
+SMTLib2Interface::SMTLib2Interface(ReadCallback::Callback const& _queryCallback):
+ m_queryCallback(_queryCallback)
+{
+ reset();
+}
+
+void SMTLib2Interface::reset()
+{
+ m_accumulatedOutput.clear();
+ m_accumulatedOutput.emplace_back();
+ write("(set-option :produce-models true)");
+ write("(set-logic QF_UFLIA)");
+}
+
+void SMTLib2Interface::push()
+{
+ m_accumulatedOutput.emplace_back();
+}
+
+void SMTLib2Interface::pop()
+{
+ 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") +
+ ")"
+ );
+ return SolverInterface::newFunction(move(_name), _domain, _codomain);
+}
+
+Expression SMTLib2Interface::newInteger(string _name)
+{
+ write("(declare-const |" + _name + "| Int)");
+ return SolverInterface::newInteger(move(_name));
+}
+
+Expression SMTLib2Interface::newBool(string _name)
+{
+ write("(declare-const |" + _name + "| Bool)");
+ return SolverInterface::newBool(std::move(_name));
+}
+
+void SMTLib2Interface::addAssertion(Expression const& _expr)
+{
+ write("(assert " + toSExpr(_expr) + ")");
+}
+
+pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<Expression> const& _expressionsToEvaluate)
+{
+ string response = querySolver(
+ boost::algorithm::join(m_accumulatedOutput, "\n") +
+ checkSatAndGetValuesCommand(_expressionsToEvaluate)
+ );
+
+ CheckResult result;
+ // TODO proper parsing
+ if (boost::starts_with(response, "sat\n"))
+ result = CheckResult::SATISFIABLE;
+ else if (boost::starts_with(response, "unsat\n"))
+ result = CheckResult::UNSATISFIABLE;
+ else if (boost::starts_with(response, "unknown\n"))
+ result = CheckResult::UNKNOWN;
+ else
+ result = CheckResult::ERROR;
+
+ vector<string> values;
+ if (result != CheckResult::UNSATISFIABLE && result != CheckResult::ERROR)
+ values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend());
+ 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(), "");
+ m_accumulatedOutput.back() += move(_data) + "\n";
+}
+
+string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _expressionsToEvaluate)
+{
+ 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) + "| " + toSExpr(e) + "))\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<string> SMTLib2Interface::parseValues(string::const_iterator _start, string::const_iterator _end)
+{
+ vector<string> 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;
+}
+
+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
new file mode 100644
index 00000000..b8dac366
--- /dev/null
+++ b/libsolidity/formal/SMTLib2Interface.h
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/formal/SolverInterface.h>
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ReadFile.h>
+
+#include <libdevcore/Common.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+#include <cstdio>
+
+namespace dev
+{
+namespace solidity
+{
+namespace smt
+{
+
+class SMTLib2Interface: public SolverInterface, public boost::noncopyable
+{
+public:
+ SMTLib2Interface(ReadCallback::Callback const& _queryCallback);
+
+ 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<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
+
+private:
+ std::string toSExpr(Expression const& _expr);
+
+ void write(std::string _data);
+
+ std::string checkSatAndGetValuesCommand(std::vector<Expression> const& _expressionsToEvaluate);
+ std::vector<std::string> parseValues(std::string::const_iterator _start, std::string::const_iterator _end);
+
+ /// Communicates with the solver via the callback. Throws SMTSolverError on error.
+ std::string querySolver(std::string const& _input);
+
+ ReadCallback::Callback m_queryCallback;
+ std::vector<std::string> m_accumulatedOutput;
+};
+
+}
+}
+}
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
new file mode 100644
index 00000000..32d92a2a
--- /dev/null
+++ b/libsolidity/formal/SolverInterface.h
@@ -0,0 +1,178 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ReadFile.h>
+
+#include <libdevcore/Common.h>
+#include <libdevcore/Exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+#include <cstdio>
+
+namespace dev
+{
+namespace solidity
+{
+namespace smt
+{
+
+enum class CheckResult
+{
+ SATISFIABLE, UNSATISFIABLE, 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;
+
+ static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
+ {
+ return Expression("ite", std::vector<Expression>{
+ std::move(_condition), std::move(_trueValue), std::move(_falseValue)
+ });
+ }
+
+ 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<Expression> const arguments;
+
+private:
+ /// Manual constructor, should only be used by SolverInterface and this class itself.
+ Expression(std::string _name, std::vector<Expression> _arguments):
+ name(std::move(_name)), arguments(std::move(_arguments)) {}
+
+ explicit Expression(std::string _name):
+ Expression(std::move(_name), std::vector<Expression>{}) {}
+ Expression(std::string _name, Expression _arg):
+ Expression(std::move(_name), std::vector<Expression>{std::move(_arg)}) {}
+ Expression(std::string _name, Expression _arg1, Expression _arg2):
+ Expression(std::move(_name), std::vector<Expression>{std::move(_arg1), std::move(_arg2)}) {}
+};
+
+DEV_SIMPLE_EXCEPTION(SolverError);
+
+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;
+
+ /// Checks for satisfiability, evaluates the expressions if a model
+ /// is available. Throws SMTSolverError on error.
+ virtual std::pair<CheckResult, std::vector<std::string>>
+ check(std::vector<Expression> const& _expressionsToEvaluate) = 0;
+};
+
+
+}
+}
+}
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
new file mode 100644
index 00000000..522928f0
--- /dev/null
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -0,0 +1,189 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/formal/Z3Interface.h>
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <libdevcore/CommonIO.h>
+
+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<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate)
+{
+// cout << "---------------------------------" << endl;
+// cout << m_solver << endl;
+ CheckResult result;
+ switch (m_solver.check())
+ {
+ case z3::check_result::sat:
+ result = CheckResult::SATISFIABLE;
+ cout << "sat" << endl;
+ break;
+ case z3::check_result::unsat:
+ result = CheckResult::UNSATISFIABLE;
+ cout << "unsat" << endl;
+ break;
+ case z3::check_result::unknown:
+ result = CheckResult::UNKNOWN;
+ cout << "unknown" << endl;
+ break;
+ default:
+ solAssert(false, "");
+ }
+// cout << "---------------------------------" << endl;
+
+
+ vector<string> values;
+ if (result != CheckResult::UNSATISFIABLE)
+ {
+ 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<string, unsigned> arity{
+ {"ite", 3},
+ {"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 == "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];
+ 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 00000000..44d4bb2f
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/formal/SolverInterface.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <z3++.h>
+
+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<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
+
+private:
+ z3::expr toZ3Expr(Expression const& _expr);
+ z3::sort z3Sort(smt::Sort _sort);
+
+ std::string checkSatAndGetValuesCommand(std::vector<Expression> const& _expressionsToEvaluate);
+ std::vector<std::string> parseValues(std::string::const_iterator _start, std::string::const_iterator _end);
+
+ z3::context m_context;
+ z3::solver m_solver;
+ std::map<std::string, z3::expr> m_constants;
+ std::map<std::string, z3::func_decl> m_functions;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 74743737..6d0c0255 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)
{
}
@@ -127,7 +127,7 @@ public:
}
private:
- LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
+ static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
{
u256 id = _tag.data();
solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 133f70b1..d84fe999 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -23,6 +23,9 @@
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/ErrorReporter.h>
+
+#include <boost/algorithm/string.hpp>
+
#include <ctype.h>
#include <algorithm>
@@ -33,6 +36,7 @@ using namespace dev::solidity::assembly;
shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
+ m_recursionDepth = 0;
try
{
m_scanner = _scanner;
@@ -48,6 +52,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann
assembly::Block Parser::parseBlock()
{
+ RecursionGuard recursionGuard(*this);
assembly::Block block = createWithLocation<Block>();
expectToken(Token::LBrace);
while (currentToken() != Token::RBrace)
@@ -59,6 +64,7 @@ assembly::Block Parser::parseBlock()
assembly::Statement Parser::parseStatement()
{
+ RecursionGuard recursionGuard(*this);
switch (currentToken())
{
case Token::Let:
@@ -155,6 +161,7 @@ assembly::Statement Parser::parseStatement()
assembly::Case Parser::parseCase()
{
+ RecursionGuard recursionGuard(*this);
assembly::Case _case = createWithLocation<assembly::Case>();
if (m_scanner->currentToken() == Token::Default)
m_scanner->next();
@@ -175,6 +182,7 @@ assembly::Case Parser::parseCase()
assembly::ForLoop Parser::parseForLoop()
{
+ RecursionGuard recursionGuard(*this);
ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For);
forLoop.pre = parseBlock();
@@ -189,6 +197,7 @@ assembly::ForLoop Parser::parseForLoop()
assembly::Statement Parser::parseExpression()
{
+ RecursionGuard recursionGuard(*this);
Statement operation = parseElementaryOperation(true);
if (operation.type() == typeid(Instruction))
{
@@ -251,6 +260,7 @@ std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
+ RecursionGuard recursionGuard(*this);
Statement ret;
switch (currentToken())
{
@@ -297,6 +307,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:
@@ -337,6 +349,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
+ RecursionGuard recursionGuard(*this);
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let);
while (true)
@@ -361,6 +374,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
+ RecursionGuard recursionGuard(*this);
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
expectToken(Token::Function);
funDef.name = expectAsmIdentifier();
@@ -392,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");
@@ -474,6 +489,7 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
TypedName Parser::parseTypedName()
{
+ RecursionGuard recursionGuard(*this);
TypedName typedName = createWithLocation<TypedName>();
typedName.name = expectAsmIdentifier();
if (m_julia)
@@ -501,3 +517,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 45708afd..e46d1732 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -45,7 +45,7 @@ public:
protected:
/// Creates an inline assembly node with the given source location.
- template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation())
+ template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const
{
T r;
r.location = _loc;
@@ -75,6 +75,8 @@ protected:
TypedName parseTypedName();
std::string expectAsmIdentifier();
+ static bool isValidNumberLiteral(std::string const& _literal);
+
private:
bool m_julia = false;
};
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 4f96a3e9..47ede91d 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 f57dddc8..66520632 100644
--- a/libsolidity/inlineasm/AsmPrinter.h
+++ b/libsolidity/inlineasm/AsmPrinter.h
@@ -53,7 +53,7 @@ public:
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 315d5953..64d5bd9a 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 cc240565..447d6490 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/ABI.cpp b/libsolidity/interface/ABI.cpp
index 12f958fc..3df9d1f8 100644
--- a/libsolidity/interface/ABI.cpp
+++ b/libsolidity/interface/ABI.cpp
@@ -19,7 +19,6 @@
*/
#include <libsolidity/interface/ABI.h>
-#include <boost/range/irange.hpp>
#include <libsolidity/ast/AST.h>
using namespace std;
@@ -36,8 +35,10 @@ 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::Pure || it.second->stateMutability() == StateMutability::View;
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/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 7c66a843..363f45dd 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -37,6 +37,7 @@
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/formal/SMTChecker.h>
#include <libsolidity/interface/ABI.h>
#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
@@ -49,8 +50,6 @@
#include <json/json.h>
#include <boost/algorithm/string.hpp>
-#include <boost/filesystem.hpp>
-
using namespace std;
using namespace dev;
@@ -240,6 +239,13 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ SMTChecker smtChecker(m_errorReporter, m_smtQuery);
+ for (Source const* source: m_sourceOrder)
+ smtChecker.analyze(*source->ast);
+ }
+
+ if (noErrors)
+ {
m_stackState = AnalysisSuccessful;
return true;
}
@@ -406,39 +412,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<Json::Value const>* 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;
+}
- return *(*doc);
+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 *_contract.devDocumentation;
}
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
@@ -526,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;
}
@@ -632,6 +641,17 @@ string CompilerStack::absolutePath(string const& _path, string const& _reference
return result.generic_string();
}
+namespace
+{
+bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)
+{
+ for (auto const feature: features)
+ if (!ExperimentalFeatureOnlyAnalysis.count(feature))
+ return false;
+ return true;
+}
+}
+
void CompilerStack::compileContract(
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
@@ -649,10 +669,23 @@ void CompilerStack::compileContract(
shared_ptr<Compiler> compiler = make_shared<Compiler>(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();
+ 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} +
+ 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);
@@ -795,8 +828,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 d287f224..361b8a45 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.
@@ -88,13 +82,13 @@ public:
/// 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) {}
/// @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; }
@@ -203,11 +197,13 @@ public:
/// 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 @@ private:
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.
@@ -290,7 +287,8 @@ private:
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<std::string, h160> m_libraries;
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index 42b0c8b6..241d6b43 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);
@@ -75,8 +75,8 @@ public:
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);
diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h
index 2e8a6bd8..7068629d 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<Result(std::string const&)>;
};
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index dd135ce5..be823743 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)
@@ -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/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h
index dfaf88cd..11a0b4c2 100644
--- a/libsolidity/interface/StandardCompiler.h
+++ b/libsolidity/interface/StandardCompiler.h
@@ -40,7 +40,7 @@ public:
/// 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(ReadCallback::Callback const& _readFile = ReadCallback::Callback())
: m_compilerStack(_readFile), m_readFile(_readFile)
{
}
@@ -56,7 +56,7 @@ private:
Json::Value compileInternal(Json::Value const& _input);
CompilerStack m_compilerStack;
- ReadFile::Callback m_readFile;
+ ReadCallback::Callback m_readFile;
};
}
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index b98991f3..ddfdb667 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<ASTNode> const& _childNode):
m_parser(_parser), m_location(_childNode->location()) {}
@@ -68,6 +68,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
try
{
+ m_recursionDepth = 0;
m_scanner = _scanner;
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<ASTNode>> nodes;
@@ -90,6 +91,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
fatalParserError(string("Expected pragma, import directive or contract/interface/library definition."));
}
}
+ solAssert(m_recursionDepth == 0, "");
return nodeFactory.createNode<SourceUnit>(nodes);
}
catch (FatalError const&)
@@ -102,6 +104,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{
+ RecursionGuard recursionGuard(*this);
// pragma anything* ;
// Currently supported:
// pragma solidity ^0.4.0 || ^0.3.0;
@@ -132,6 +135,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
+ RecursionGuard recursionGuard(*this);
// import "abc" [as x];
// import * as x from "abc";
// import {a as b, c} from "abc";
@@ -212,6 +216,7 @@ ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token
ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
@@ -275,6 +280,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName());
vector<ASTPointer<Expression>> arguments;
@@ -307,8 +313,25 @@ 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;
+ // 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();
+ return stateMutability;
+}
+
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
{
+ RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
expectToken(Token::Function);
if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
@@ -321,23 +344,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
while (true)
{
Token::Value token = m_scanner->currentToken();
- if (token == Token::Const)
- {
- 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).
@@ -354,12 +361,30 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
{
if (result.visibility != Declaration::Visibility::Default)
{
- parserError(string("Multiple visibility specifiers."));
+ parserError(string(
+ "Visibility already specified as \"" +
+ Declaration::visibilityToString(result.visibility) +
+ "\"."
+ ));
m_scanner->next();
}
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;
}
@@ -376,6 +401,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->currentCommentLiteral() != "")
@@ -404,13 +430,12 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
return nodeFactory.createNode<FunctionDefinition>(
header.name,
header.visibility,
+ header.stateMutability,
c_isConstructor,
docstring,
header.parameters,
- header.isDeclaredConst,
header.modifiers,
header.returnParameters,
- header.isPayable,
block
);
}
@@ -421,8 +446,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
header.parameters,
header.returnParameters,
header.visibility,
- header.isDeclaredConst,
- header.isPayable
+ header.stateMutability
);
type = parseTypeNameSuffix(type, nodeFactory);
VarDeclParserOptions options;
@@ -436,6 +460,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
ASTPointer<StructDefinition> Parser::parseStructDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Struct);
ASTPointer<ASTString> name = expectIdentifierToken();
@@ -453,6 +478,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
ASTPointer<EnumValue> Parser::parseEnumValue()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
return nodeFactory.createNode<EnumValue>(expectIdentifierToken());
@@ -460,6 +486,7 @@ ASTPointer<EnumValue> Parser::parseEnumValue()
ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Enum);
ASTPointer<ASTString> name = expectIdentifierToken();
@@ -488,6 +515,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
ASTPointer<TypeName> type;
@@ -512,7 +540,11 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{
if (visibility != Declaration::Visibility::Default)
{
- parserError(string("Visibility already specified."));
+ parserError(string(
+ "Visibility already specified as \"" +
+ Declaration::visibilityToString(visibility) +
+ "\"."
+ ));
m_scanner->next();
}
else
@@ -522,7 +554,7 @@ ASTPointer<VariableDeclaration> 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))
{
@@ -576,6 +608,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
{
+ RecursionGuard recursionGuard(*this);
ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
m_insideModifier = true;
@@ -603,6 +636,7 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<EventDefinition> Parser::parseEventDefinition()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->currentCommentLiteral() != "")
@@ -632,6 +666,7 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
ASTPointer<UsingForDirective> Parser::parseUsingDirective()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Using);
@@ -649,6 +684,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
vector<ASTPointer<Expression>> arguments;
@@ -666,6 +702,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
ASTPointer<Identifier> Parser::parseIdentifier()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
return nodeFactory.createNode<Identifier>(expectIdentifierToken());
@@ -673,6 +710,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
vector<ASTString> identifierPath{*expectIdentifierToken()};
@@ -687,6 +725,7 @@ ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory)
{
+ RecursionGuard recursionGuard(*this);
while (m_scanner->currentToken() == Token::LBrack)
{
m_scanner->next();
@@ -702,6 +741,7 @@ ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTN
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type;
Token::Value token = m_scanner->currentToken();
@@ -737,19 +777,20 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
ASTPointer<FunctionTypeName> Parser::parseFunctionType()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
FunctionHeaderParserResult header = parseFunctionHeader(true, false);
return nodeFactory.createNode<FunctionTypeName>(
header.parameters,
header.returnParameters,
header.visibility,
- header.isDeclaredConst,
- header.isPayable
+ header.stateMutability
);
}
ASTPointer<Mapping> Parser::parseMapping()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Mapping);
expectToken(Token::LParen);
@@ -776,6 +817,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
bool _allowEmpty
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options(_options);
@@ -797,6 +839,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBrace);
vector<ASTPointer<Statement>> statements;
@@ -809,6 +852,7 @@ ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString)
ASTPointer<Statement> Parser::parseStatement()
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
@@ -871,6 +915,7 @@ ASTPointer<Statement> Parser::parseStatement()
ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Assembly);
if (m_scanner->currentToken() == Token::StringLiteral)
@@ -888,6 +933,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::If);
expectToken(Token::LParen);
@@ -908,6 +954,7 @@ ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _d
ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::While);
expectToken(Token::LParen);
@@ -920,6 +967,7 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> con
ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Do);
ASTPointer<Statement> body = parseStatement();
@@ -935,6 +983,7 @@ ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> c
ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression;
@@ -968,6 +1017,7 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const&
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> 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.
@@ -1030,6 +1080,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
if (_lookAheadArrayType)
nodeFactory.setLocation(_lookAheadArrayType->location());
@@ -1093,6 +1144,7 @@ ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(_docString, expression);
}
@@ -1101,6 +1153,7 @@ ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
if (Token::isAssignmentOp(m_scanner->currentToken()))
{
@@ -1129,6 +1182,7 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->currentToken());
@@ -1148,6 +1202,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
Token::Value token = m_scanner->currentToken();
@@ -1176,6 +1231,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure
)
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
@@ -1233,6 +1289,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> Parser::parsePrimaryExpression()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->currentToken();
ASTPointer<Expression> expression;
@@ -1292,10 +1349,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
parserError("Expected expression (inline array elements cannot be omitted).");
else
components.push_back(ASTPointer<Expression>());
+
if (m_scanner->currentToken() == oppositeToken)
break;
- else if (m_scanner->currentToken() == Token::Comma)
- m_scanner->next();
+
+ expectToken(Token::Comma);
}
nodeFactory.markEndPosition();
expectToken(oppositeToken);
@@ -1322,6 +1380,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{
+ RecursionGuard recursionGuard(*this);
vector<ASTPointer<Expression>> arguments;
if (m_scanner->currentToken() != Token::RParen)
{
@@ -1337,6 +1396,7 @@ vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
{
+ RecursionGuard recursionGuard(*this);
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
Token::Value token = m_scanner->currentToken();
if (token == Token::LBrace)
@@ -1403,6 +1463,7 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
)
{
solAssert(!_path.empty(), "");
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
SourceLocation location = _path.front()->location();
location.end = _path.back()->location().end;
@@ -1435,6 +1496,7 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
)
{
solAssert(!_path.empty(), "");
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this, _path.front());
ASTPointer<Expression> expression(_path.front());
for (size_t i = 1; i < _path.size(); ++i)
@@ -1458,6 +1520,7 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<ParameterList> Parser::createEmptyParameterList()
{
+ RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 19631c58..cfdfea7e 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<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
@@ -60,8 +60,7 @@ private:
ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters;
Declaration::Visibility visibility = Declaration::Visibility::Default;
- bool isDeclaredConst = false;
- bool isPayable = false;
+ StateMutability stateMutability = StateMutability::NonPayable;
std::vector<ASTPointer<ModifierInvocation>> modifiers;
};
@@ -73,6 +72,7 @@ private:
ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
+ StateMutability parseStateMutability(Token::Value _token);
FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 5657c2c0..fe95b0fe 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 >= 3000)
+ 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 5b03ab5e..fd0de0d1 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -36,11 +36,25 @@ class Scanner;
class ParserBase
{
public:
- ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {}
+ explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {}
std::shared_ptr<std::string const> 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 @@ protected:
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 @@ protected:
std::shared_ptr<Scanner> 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/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h
index d6b48c6f..0adaa6fd 100644
--- a/libsolidity/parsing/Scanner.h
+++ b/libsolidity/parsing/Scanner.h
@@ -75,7 +75,7 @@ public:
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 @@ public:
///@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 @@ private:
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
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index d412b3f0..805fbf5d 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) \
@@ -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) \
@@ -176,6 +177,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 */ \
@@ -229,14 +231,12 @@ 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) \
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,6 +290,7 @@ public:
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 == 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/lllc/CMakeLists.txt b/lllc/CMakeLists.txt
index 3bd11187..7bebd0b1 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)
diff --git a/scripts/Dockerfile b/scripts/Dockerfile
index c984ce99..654a9f29 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 <sys\/poll.h>/include <poll.h>/' /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/scripts/install_deps.sh b/scripts/install_deps.sh
index 24cf49d5..3a1abe10 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,8 @@ 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 +382,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 +399,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
diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh
index 4fae90ef..cb3519b0 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) <builds@ethereum.org>
-Build-Depends: debhelper (>= 9.0.0),
+Build-Depends: ${Z3DEPENDENCY}debhelper (>= 9.0.0),
cmake,
g++-4.8,
git,
diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh
index a2dbe61c..f1d44a1f 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
@@ -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/snap/snapcraft.yaml b/snap/snapcraft.yaml
index db976bc3..ea0f2ef5 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
diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt
index 18e83e75..656b27c3 100644
--- a/solc/CMakeLists.txt
+++ b/solc/CMakeLists.txt
@@ -1,27 +1,31 @@
-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)
+
+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
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 740061a1..315f951e 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);
@@ -662,7 +663,7 @@ Allowed options)",
bool CommandLineInterface::processInput()
{
- ReadFile::Callback fileReader = [this](string const& _path)
+ ReadCallback::Callback fileReader = [this](string const& _path)
{
try
{
@@ -682,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."};
}
};
@@ -850,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"))
@@ -881,10 +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));
- output[g_strContracts][contractName] = contractData;
+ contractData[g_strNatspecUser] = dev::jsonCompactPrint(m_compiler->natspecUser(contractName));
}
bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime);
@@ -1170,8 +1170,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 8a476ef5..bf9400e4 100644
--- a/solc/CommandLineInterface.h
+++ b/solc/CommandLineInterface.h
@@ -66,7 +66,7 @@ private:
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/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
index de797b3c..684d49e4 100644
--- a/solc/jsonCompiler.cpp
+++ b/solc/jsonCompiler.cpp
@@ -38,9 +38,12 @@ extern "C" {
typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error);
}
-ReadFile::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr)
+namespace
{
- ReadFile::Callback readCallback;
+
+ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = nullptr)
+{
+ ReadCallback::Callback readCallback;
if (_readCallback)
{
readCallback = [=](string const& _path)
@@ -48,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;
@@ -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 c61da6e9..6d559542 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, ""))
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8e7b8916..6a8a4399 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 ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
+add_executable(soltest ${sources} ${headers})
+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 ${Boost_PROGRAM_OPTIONS_LIBRARIES})
+target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES})
diff --git a/test/Metadata.cpp b/test/Metadata.cpp
index 03f905b1..e4de0a6b 100644
--- a/test/Metadata.cpp
+++ b/test/Metadata.cpp
@@ -21,7 +21,6 @@
#include <string>
#include <iostream>
-#include <regex>
#include <libdevcore/JSON.h>
using namespace std;
diff --git a/test/RPCSession.h b/test/RPCSession.h
index f7fdd86e..558cb99f 100644
--- a/test/RPCSession.h
+++ b/test/RPCSession.h
@@ -124,7 +124,7 @@ public:
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
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
index c2121940..d8c5b678 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/externalTests.sh b/test/externalTests.sh
new file mode 100755
index 00000000..1b74561b
--- /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 <http://www.gnu.org/licenses/>
+#
+# (c) 2016 solidity contributors.
+#------------------------------------------------------------------------------
+
+set -e
+
+if [ ! -f "$1" ]
+then
+ echo "Usage: $0 <path to soljson.js>"
+ 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"
diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp
new file mode 100644
index 00000000..a694aa1b
--- /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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Unit tests for IndentedWriter.
+ */
+
+#include <libdevcore/IndentedWriter.h>
+
+#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()
+
+}
+}
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index 5aa81af5..6656f15b 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -29,7 +29,6 @@
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
-#include <chrono>
#include <string>
#include <tuple>
#include <memory>
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index dd6f3d94..51070370 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 }"));
@@ -229,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, "recursion");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp
new file mode 100644
index 00000000..297c4ef0
--- /dev/null
+++ b/test/libsolidity/ABIEncoderTests.cpp
@@ -0,0 +1,405 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Unit tests for Solidity's ABI encoder.
+ */
+
+#include <functional>
+#include <string>
+#include <tuple>
+#include <boost/test/unit_test.hpp>
+#include <libsolidity/interface/Exceptions.h>
+#include <test/libsolidity/SolidityExecutionFramework.h>
+
+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)
+
+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)
+{
+ string 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);
+ }
+ }
+ )";
+ 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)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(string, bytes20, string);
+ function f() {
+ E("abcdef", "abcde", "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)
+{
+ string sourceCode = R"(
+ contract C {
+ enum E { A, B }
+ function f(uint x) returns (E en) {
+ assembly { en := x }
+ }
+ }
+ )";
+ 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)
+{
+ string 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));
+ }
+ }
+ )";
+ 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)
+{
+ string 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()");
+ // 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)
+{
+ string 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);
+ }
+ }
+ )";
+ 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)
+{
+ string 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);
+ }
+ }
+ )";
+ 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)
+{
+ string sourceCode = R"(
+ contract C {
+ bytes short;
+ bytes long;
+ event E(bytes s, bytes l);
+ function f() {
+ short = "123456789012345678901234567890a";
+ long = "ffff123456789012345678901234567890afffffffff123456789012345678901234567890a";
+ E(short, long);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(
+ 0x40, 0x80,
+ 31, string("123456789012345678901234567890a"),
+ 75, string("ffff123456789012345678901234567890afffffffff123456789012345678901234567890a")
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_array)
+{
+ string 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);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f()");
+ REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3)));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(storage_array_dyn)
+{
+ string sourceCode = R"(
+ contract C {
+ address[] addr;
+ event E(address[] a);
+ function f() {
+ addr.push(1);
+ addr.push(2);
+ addr.push(3);
+ E(addr);
+ }
+ }
+ )";
+ 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)
+{
+ string 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);
+ }
+ }
+ )";
+ 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)
+{
+ string 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);
+ }
+ }
+ )";
+ 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)
+{
+ 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.
+ 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);
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode);
+ callContractFunction("f(uint256)");
+ REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1))));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(calldata)
+{
+ string sourceCode = R"(
+ contract C {
+ event E(bytes);
+ function f(bytes a) external {
+ E(a);
+ }
+ }
+ )";
+ string s("abcdef");
+ string t("abcdefgggggggggggggggggggggggggggggggggggggggghhheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeggg");
+ 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()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index 4fb4f20c..31165922 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/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 4bf4eb48..0debc66d 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 { } }"));
@@ -392,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)
diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp
index 536ba730..0fe7636c 100644
--- a/test/libsolidity/JSONCompiler.cpp
+++ b/test/libsolidity/JSONCompiler.cpp
@@ -20,8 +20,6 @@
*/
#include <string>
-#include <iostream>
-#include <regex>
#include <boost/test/unit_test.hpp>
#include <libdevcore/JSON.h>
#include <libsolidity/interface/Version.h>
diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp
index 0d3caddd..c46e3160 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) {}
}
@@ -58,6 +59,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;
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index 452a2662..0512ba1f 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": [
{
@@ -244,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 {
@@ -258,6 +320,7 @@ BOOST_AUTO_TEST_CASE(const_function)
"name": "foo",
"constant": false,
"payable" : false,
+ "stateMutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -280,6 +343,62 @@ BOOST_AUTO_TEST_CASE(const_function)
"name": "boo",
"constant": true,
"payable" : false,
+ "stateMutability": "view",
+ "type": "function",
+ "inputs": [{
+ "name": "a",
+ "type": "uint32"
+ }],
+ "outputs": [
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ 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",
@@ -311,6 +430,7 @@ BOOST_AUTO_TEST_CASE(events)
"name": "f",
"constant": false,
"payable" : false,
+ "stateMutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -392,6 +512,7 @@ BOOST_AUTO_TEST_CASE(inherited)
"name": "baseFunction",
"constant": false,
"payable" : false,
+ "stateMutability": "nonpayable",
"type": "function",
"inputs":
[{
@@ -408,6 +529,7 @@ BOOST_AUTO_TEST_CASE(inherited)
"name": "derivedFunction",
"constant": false,
"payable" : false,
+ "stateMutability": "nonpayable",
"type": "function",
"inputs":
[{
@@ -463,6 +585,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 +628,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
"name": "f",
"constant": false,
"payable" : false,
+ "stateMutability": "nonpayable",
"type": "function",
"inputs": [
{
@@ -548,12 +672,44 @@ BOOST_AUTO_TEST_CASE(constructor_abi)
}
],
"payable": false,
+ "stateMutability": "nonpayable",
"type": "constructor"
}
])";
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)
{
@@ -574,6 +730,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
{
"constant" : false,
"payable" : false,
+ "stateMutability": "nonpayable",
"inputs" : [],
"name" : "ret",
"outputs" : [
@@ -592,6 +749,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
}
],
"payable": false,
+ "stateMutability": "nonpayable",
"type": "constructor"
}
]
@@ -613,6 +771,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
{
"constant" : false,
"payable" : false,
+ "stateMutability": "nonpayable",
"name": "f",
"inputs": [
{ "name": "a", "type": "string" },
@@ -641,6 +800,7 @@ BOOST_AUTO_TEST_CASE(library_function)
{
"constant" : false,
"payable" : false,
+ "stateMutability": "nonpayable",
"name": "f",
"inputs": [
{ "name": "b", "type": "test.StructType storage" },
@@ -670,6 +830,7 @@ BOOST_AUTO_TEST_CASE(include_fallback_function)
[
{
"payable": false,
+ "stateMutability": "nonpayable",
"type" : "fallback"
}
]
@@ -691,6 +852,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
{
"constant" : false,
"payable": false,
+ "stateMutability": "nonpayable",
"inputs": [],
"name": "f",
"outputs": [],
@@ -699,6 +861,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
{
"constant" : false,
"payable": true,
+ "stateMutability": "payable",
"inputs": [],
"name": "g",
"outputs": [],
@@ -721,6 +884,7 @@ BOOST_AUTO_TEST_CASE(payable_fallback_function)
[
{
"payable": true,
+ "stateMutability": "payable",
"type" : "fallback"
}
]
@@ -741,6 +905,7 @@ BOOST_AUTO_TEST_CASE(function_type)
{
"constant" : false,
"payable": false,
+ "stateMutability": "nonpayable",
"inputs": [{
"name": "x",
"type": "function"
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index d0c5285c..73dd7d22 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,32 @@ 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)")));
+}
+
+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)")));
}
@@ -4402,6 +4427,92 @@ 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"(
+ 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));
+ 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;
+ }
+ 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);
+ 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_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)
+{
+ // 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"(
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 2fbc6ac8..380978e8 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,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, "");
+ 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, "");
+ CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"view\" to \"nonpayable\".");
+}
+
+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 changes state mutability from \"nonpayable\" to \"view\".");
}
BOOST_AUTO_TEST_CASE(complex_inheritance)
@@ -1003,7 +1012,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 +1029,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 +1052,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 +1115,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 +1126,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 +1137,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)
@@ -1163,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());
@@ -1171,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());
@@ -1180,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)
@@ -1194,7 +1203,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 +1238,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 +1264,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 +1293,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 +1310,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 +1332,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 +1342,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 +1352,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 +1363,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier)
function() constant { x = 2; }
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Fallback function must be payable or non-payable");
}
BOOST_AUTO_TEST_CASE(fallback_function_twice)
@@ -1366,7 +1375,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 +1410,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 +1430,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 +1549,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 +1562,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 +1590,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 +1605,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 +1620,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 +1635,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 +1689,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 +1699,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 +1722,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 +1732,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)
@@ -1756,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)
{
@@ -1837,7 +1884,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 +1898,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 +1944,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 +1958,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 +1972,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 +1982,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 +2012,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 +2025,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 +2036,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 +2049,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 +2059,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 +2069,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 +2079,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 +2102,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 +2124,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 +2136,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 +2172,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 +2182,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 +2192,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 +2353,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 +2463,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 +2475,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 +2488,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 +2511,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 +2525,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 +2538,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types)
function f(uint a) returns (uint8) { }
}
)";
- CHECK_ERROR(sourceCode, TypeError, "");
+ CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ");
}
BOOST_AUTO_TEST_CASE(multiple_constructors)
@@ -2502,7 +2549,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 +2560,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 +2570,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 +2622,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 +2633,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 +2643,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 +2663,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 +2683,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 +2693,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 +2701,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 +2717,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 +2733,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 +2747,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 +2757,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 +2784,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 +2798,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 +2815,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 +2855,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 +2882,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 +2911,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 +2926,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 +2939,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 +3009,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 +3035,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 +3044,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 +3052,17 @@ 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(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)
@@ -3038,7 +3095,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 +3109,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 +3119,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 +3149,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 +3159,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 +3169,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 +3209,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 +3219,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 +3230,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 +3241,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 +3269,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 +3280,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 +3291,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 +3359,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 +3450,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 +3466,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 +3477,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 +3492,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 +3539,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 +3551,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 +3563,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 +3683,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 +3708,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 +3721,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 +3734,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 +3749,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 +3761,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 +3775,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 +3795,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 +3810,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 +3828,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 +3843,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 +3950,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 +4740,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 +4750,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 +4770,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 +4780,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 +4789,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, "Overriding function changes state mutability from \"payable\" to \"nonpayable\".");
}
BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
@@ -4741,7 +4798,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, "Overriding function changes state mutability from \"nonpayable\" to \"payable\".");
}
BOOST_AUTO_TEST_CASE(function_variable_mixin)
@@ -4759,16 +4816,7 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin)
function checkOk() returns (bool) { return ok(); }
}
)";
- CHECK_ERROR(text, DeclarationError, "");
-}
-
-
-BOOST_AUTO_TEST_CASE(payable_constant_conflict)
-{
- char const* text = R"(
- contract C { function f() payable constant {} }
- )";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
}
BOOST_AUTO_TEST_CASE(calling_payable)
@@ -4792,7 +4840,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 +4856,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 +4883,7 @@ BOOST_AUTO_TEST_CASE(constant_constructor)
function test() constant {}
}
)";
- CHECK_ERROR(text, TypeError, "");
+ CHECK_ERROR(text, TypeError, "Constructor must be payable or non-payable");
}
BOOST_AUTO_TEST_CASE(external_constructor)
@@ -4845,7 +4893,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 +4904,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 +4923,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 +4971,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 +4983,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 +4993,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 +5006,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 +5016,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 +5026,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 +5052,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 +5064,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 +5086,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 +5137,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 +5149,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 +5232,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 +5242,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 +5252,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 +5262,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 +5457,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)
@@ -6536,6 +6584,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"(
@@ -6558,6 +6657,93 @@ 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"(
+ 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 __test;
+ )";
+ CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments.");
+// text = R"(
+// pragma experimental __test;
+// pragma experimental __test;
+// )";
+// 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_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()
}
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
index be20a9f2..149221d5 100644
--- a/test/libsolidity/SolidityNatspecJSON.cpp
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -51,9 +51,9 @@ public:
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(
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 78edd4d1..a39e0958 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -898,25 +898,46 @@ 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 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() constant view {}
+ })";
+ 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\".");
+ 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)
@@ -1182,6 +1203,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"(
@@ -1345,6 +1378,42 @@ 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(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"(
diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp
index 0b5ab516..9f385a04 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<FunctionType>(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<MappingType>(Type::fromElementaryTypeName("bytes32"), s.type());
MappingType m2(Type::fromElementaryTypeName("uint64"), m);
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index be13d46b..79848c36 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -20,8 +20,6 @@
*/
#include <string>
-#include <iostream>
-#include <regex>
#include <boost/test/unit_test.hpp>
#include <libsolidity/interface/StandardCompiler.h>
#include <libdevcore/JSON.h>