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