mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #2802 from ethereum/develop
Merge develop into release for 0.4.16
This commit is contained in:
		
						commit
						d7661dd974
					
				| @ -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) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										30
									
								
								Changelog.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								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: | ||||
|  | ||||
							
								
								
									
										10
									
								
								circle.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								circle.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| version: 2 | ||||
| jobs: | ||||
|   build: | ||||
|     branches: | ||||
|       ignore: | ||||
|         - /.*/ | ||||
|     docker: | ||||
|       - image: trzeci/emscripten:sdk-tag-1.37.18-64bit | ||||
|     steps: | ||||
|       - checkout | ||||
| @ -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() | ||||
| @ -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) | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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() | ||||
| @ -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() | ||||
| 
 | ||||
|  | ||||
| @ -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() | ||||
| @ -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() | ||||
| @ -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() | ||||
							
								
								
									
										7
									
								
								cmake/FindZ3.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cmake/FindZ3.cmake
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||
| 
 | ||||
| @ -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() | ||||
| @ -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() | ||||
| @ -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"`. | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| --------- | ||||
| 
 | ||||
|  | ||||
| @ -354,6 +354,10 @@ | ||||
|         "bugs": [],  | ||||
|         "released": "2017-08-08" | ||||
|     },  | ||||
|     "0.4.16": { | ||||
|         "bugs": [],  | ||||
|         "released": "2017-08-24" | ||||
|     },  | ||||
|     "0.4.2": { | ||||
|         "bugs": [ | ||||
|             "DelegateCallReturnValue",  | ||||
|  | ||||
| @ -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 constant method is not modifying state. | ||||
|   The compiler does not enforce yet that a ``view`` method is not modifying state. | ||||
| 
 | ||||
| .. _pure-functions: | ||||
| 
 | ||||
| ************** | ||||
| Pure Functions | ||||
| ************** | ||||
| 
 | ||||
| Functions can be declared ``pure`` in which case they promise not to read from or modify the state. | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     pragma solidity ^0.4.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f(uint a, uint b) pure returns (uint) { | ||||
|             return a * (b + 42); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| .. warning:: | ||||
|   The compiler does not enforce yet that a ``pure`` method is not reading from the state. | ||||
| 
 | ||||
| .. index:: ! fallback function, function;fallback | ||||
| 
 | ||||
| @ -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(); | ||||
| 
 | ||||
|  | ||||
| @ -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. | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -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 | ||||
| ======== | ||||
|  | ||||
| @ -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 | ||||
| ============================ | ||||
| 
 | ||||
|  | ||||
| @ -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:: | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| ================= | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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), | ||||
|  | ||||
| @ -1,14 +1,8 @@ | ||||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") | ||||
| file(GLOB sources "*.cpp") | ||||
| file(GLOB headers "*.h") | ||||
| 
 | ||||
| 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 ) | ||||
| 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) | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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]; | ||||
|  | ||||
							
								
								
									
										65
									
								
								libdevcore/IndentedWriter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								libdevcore/IndentedWriter.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
| } | ||||
							
								
								
									
										67
									
								
								libdevcore/IndentedWriter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								libdevcore/IndentedWriter.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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 << " ???"; | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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) | ||||
| file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") | ||||
| file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") | ||||
| 
 | ||||
| set(EXECUTABLE solidity) | ||||
| 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() | ||||
| 
 | ||||
| file(GLOB HEADERS "*/*.h" "../libjulia/backends/evm/*") | ||||
| 
 | ||||
| include_directories(BEFORE ..) | ||||
| add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) | ||||
| 
 | ||||
| eth_use(${EXECUTABLE} REQUIRED Dev::soldevcore Solidity::solevmasm) | ||||
| 
 | ||||
| install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) | ||||
| add_library(solidity ${sources} ${headers}) | ||||
| target_link_libraries(solidity PUBLIC evmasm devcore) | ||||
| 
 | ||||
| if (${Z3_FOUND}) | ||||
|   target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) | ||||
| endif() | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -77,6 +77,8 @@ private: | ||||
| 	bool m_versionPragmaFound = false; | ||||
| 
 | ||||
| 	int m_inLoopDepth = 0; | ||||
| 
 | ||||
| 	SourceUnit const* m_sourceUnit = nullptr; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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."); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										54
									
								
								libsolidity/ast/ASTEnums.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								libsolidity/ast/ASTEnums.h
									
									
									
									
									
										Normal file
									
								
							| @ -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."); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -95,6 +95,5 @@ using ASTPointer = std::shared_ptr<T>; | ||||
| 
 | ||||
| using ASTString = std::string; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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--; | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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); } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
							
								
								
									
										53
									
								
								libsolidity/ast/ExperimentalFeatures.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								libsolidity/ast/ExperimentalFeatures.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 }, | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -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()) | ||||
| 	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 | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1074
									
								
								libsolidity/codegen/ABIFunctions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1074
									
								
								libsolidity/codegen/ABIFunctions.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										172
									
								
								libsolidity/codegen/ABIFunctions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								libsolidity/codegen/ABIFunctions.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -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.
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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>
 | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
							
								
								
									
										588
									
								
								libsolidity/formal/SMTChecker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								libsolidity/formal/SMTChecker.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| } | ||||
							
								
								
									
										114
									
								
								libsolidity/formal/SMTChecker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								libsolidity/formal/SMTChecker.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
							
								
								
									
										187
									
								
								libsolidity/formal/SMTLib2Interface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								libsolidity/formal/SMTLib2Interface.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
							
								
								
									
										75
									
								
								libsolidity/formal/SMTLib2Interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								libsolidity/formal/SMTLib2Interface.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| } | ||||
							
								
								
									
										178
									
								
								libsolidity/formal/SolverInterface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								libsolidity/formal/SolverInterface.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| } | ||||
| } | ||||
							
								
								
									
										189
									
								
								libsolidity/formal/Z3Interface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								libsolidity/formal/Z3Interface.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
| } | ||||
							
								
								
									
										65
									
								
								libsolidity/formal/Z3Interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								libsolidity/formal/Z3Interface.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| } | ||||
| @ -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."); | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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()) | ||||
|  | ||||
| @ -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; | ||||
| @ -238,6 +237,13 @@ bool CompilerStack::analyze() | ||||
| 				noErrors = false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (noErrors) | ||||
| 	{ | ||||
| 		SMTChecker smtChecker(m_errorReporter, m_smtQuery); | ||||
| 		for (Source const* source: m_sourceOrder) | ||||
| 			smtChecker.analyze(*source->ast); | ||||
| 	} | ||||
| 
 | ||||
| 	if (noErrors) | ||||
| 	{ | ||||
| 		m_stackState = AnalysisSuccessful; | ||||
| @ -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 *(*doc); | ||||
| 	return *_contract.userDocumentation; | ||||
| } | ||||
| 
 | ||||
| Json::Value const& CompilerStack::natspecDev(string const& _contractName) const | ||||
| { | ||||
| 	return natspecDev(contract(_contractName)); | ||||
| } | ||||
| 
 | ||||
| Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const | ||||
| { | ||||
| 	if (m_stackState < AnalysisSuccessful) | ||||
| 		BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); | ||||
| 
 | ||||
| 	solAssert(_contract.contract, ""); | ||||
| 
 | ||||
| 	// caches the result
 | ||||
| 	if (!_contract.devDocumentation) | ||||
| 		_contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); | ||||
| 
 | ||||
| 	return *_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); | ||||
| } | ||||
|  | ||||
| @ -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.
 | ||||
| 	/// @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& natspecUser(std::string const& _contractName) const; | ||||
| 
 | ||||
| 	/// @returns a JSON representing the contract's developer documentation.
 | ||||
| 	/// Prerequisite: Successful call to parse or compile.
 | ||||
| 	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; | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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&)>; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user