mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5697 from ethereum/develop
Merge develop into release for 0.5.2
This commit is contained in:
commit
1df8f40cd2
@ -15,7 +15,7 @@ defaults:
|
|||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
[ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON"
|
[ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON"
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS
|
cmake .. -DCMAKE_BUILD_TYPE=Release $CMAKE_OPTIONS
|
||||||
make -j4
|
make -j4
|
||||||
- run_tests: &run_tests
|
- run_tests: &run_tests
|
||||||
name: Tests
|
name: Tests
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
- version.txt
|
- version.txt
|
||||||
test_emscripten_solcjs:
|
test_emscripten_solcjs:
|
||||||
docker:
|
docker:
|
||||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
- image: circleci/node:10
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
@ -78,23 +78,15 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Install external tests deps
|
name: Install external tests deps
|
||||||
command: |
|
command: |
|
||||||
apt-get -qq update
|
|
||||||
apt-get -qy install netcat curl
|
|
||||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
|
|
||||||
export NVM_DIR="/usr/local/nvm"
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
|
||||||
nvm --version
|
|
||||||
nvm install 8
|
|
||||||
node --version
|
node --version
|
||||||
npm --version
|
npm --version
|
||||||
- run:
|
- run:
|
||||||
name: Test solcjs
|
name: Test solcjs
|
||||||
command: |
|
command: |
|
||||||
. /usr/local/nvm/nvm.sh
|
|
||||||
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
|
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
|
||||||
test_emscripten_external:
|
test_emscripten_external:
|
||||||
docker:
|
docker:
|
||||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
- image: circleci/node:10
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
@ -104,19 +96,11 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Install external tests deps
|
name: Install external tests deps
|
||||||
command: |
|
command: |
|
||||||
apt-get -qq update
|
|
||||||
apt-get -qy install netcat curl
|
|
||||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
|
|
||||||
export NVM_DIR="/usr/local/nvm"
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
|
||||||
nvm --version
|
|
||||||
nvm install 8
|
|
||||||
node --version
|
node --version
|
||||||
npm --version
|
npm --version
|
||||||
- run:
|
- run:
|
||||||
name: External tests
|
name: External tests
|
||||||
command: |
|
command: |
|
||||||
. /usr/local/nvm/nvm.sh
|
|
||||||
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
|
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
|
||||||
build_x86_linux:
|
build_x86_linux:
|
||||||
docker:
|
docker:
|
||||||
|
39
.travis.yml
39
.travis.yml
@ -55,6 +55,18 @@ matrix:
|
|||||||
env:
|
env:
|
||||||
- ZIP_SUFFIX=ubuntu-trusty
|
- ZIP_SUFFIX=ubuntu-trusty
|
||||||
- SOLC_STOREBYTECODE=On
|
- SOLC_STOREBYTECODE=On
|
||||||
|
before_install:
|
||||||
|
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||||
|
- sudo add-apt-repository -y ppa:mhier/libboost-latest
|
||||||
|
- sudo add-apt-repository -y ppa:hvr/z3
|
||||||
|
- sudo apt-get update -qq
|
||||||
|
install:
|
||||||
|
- sudo apt-get install -qq g++-8 gcc-8
|
||||||
|
- sudo apt-get install -qq libboost1.67-dev
|
||||||
|
- sudo apt-get install -qq libleveldb1
|
||||||
|
- sudo apt-get install -qq libz3-dev
|
||||||
|
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
|
||||||
|
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@ -63,6 +75,18 @@ matrix:
|
|||||||
env:
|
env:
|
||||||
- ZIP_SUFFIX=ubuntu-trusty-clang
|
- ZIP_SUFFIX=ubuntu-trusty-clang
|
||||||
- SOLC_STOREBYTECODE=On
|
- SOLC_STOREBYTECODE=On
|
||||||
|
before_install:
|
||||||
|
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||||
|
- sudo add-apt-repository -y ppa:mhier/libboost-latest
|
||||||
|
- sudo add-apt-repository -y ppa:hvr/z3
|
||||||
|
- sudo apt-get update -qq
|
||||||
|
install:
|
||||||
|
- sudo apt-get install -qq g++-8 gcc-8
|
||||||
|
- sudo apt-get install -qq libboost1.67-dev
|
||||||
|
- sudo apt-get install -qq libleveldb1
|
||||||
|
- sudo apt-get install -qq libz3-dev
|
||||||
|
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
|
||||||
|
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90
|
||||||
|
|
||||||
# Docker target, which generates a statically linked alpine image
|
# Docker target, which generates a statically linked alpine image
|
||||||
- os: linux
|
- os: linux
|
||||||
@ -159,18 +183,15 @@ cache:
|
|||||||
install:
|
install:
|
||||||
- test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh)
|
- test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh)
|
||||||
- test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh)
|
- test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh)
|
||||||
# Disable tests unless run on the release branch, on tags or with daily cron
|
|
||||||
#- if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" -a "$TRAVIS_EVENT_TYPE" != cron ]; then SOLC_TESTS=Off; fi
|
|
||||||
- SOLC_TESTS=Off
|
|
||||||
- if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
|
|
||||||
- echo -n "$TRAVIS_COMMIT" > commit_hash.txt
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
# Disable tests unless run on the release branch, on tags or with daily cron
|
||||||
|
- if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" -a "$TRAVIS_EVENT_TYPE" != cron ]; then SOLC_TESTS=Off; fi
|
||||||
|
- if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
|
||||||
|
- echo -n "$TRAVIS_COMMIT" > commit_hash.txt
|
||||||
- test $SOLC_EMSCRIPTEN != On || (scripts/build_emscripten.sh)
|
- test $SOLC_EMSCRIPTEN != On || (scripts/build_emscripten.sh)
|
||||||
- test $SOLC_DOCKER != On || (scripts/docker_build.sh)
|
- test $SOLC_DOCKER != On || (scripts/docker_build.sh)
|
||||||
- test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE
|
- test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE -DBoost_USE_STATIC_LIBS=OFF && scripts/create_source_tarball.sh)
|
||||||
&& scripts/release.sh $ZIP_SUFFIX
|
|
||||||
&& scripts/create_source_tarball.sh)
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh)
|
- test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh)
|
||||||
@ -204,7 +225,7 @@ deploy:
|
|||||||
- release
|
- release
|
||||||
- /^v\d/
|
- /^v\d/
|
||||||
# This is the deploy target for the native build (Linux and macOS)
|
# This is the deploy target for the native build (Linux and macOS)
|
||||||
# which generates ZIPs per commit and the source tarball.
|
# which generates the source tarball.
|
||||||
#
|
#
|
||||||
# This runs for each tag that is created and adds the corresponding files.
|
# This runs for each tag that is created and adds the corresponding files.
|
||||||
- provider: releases
|
- provider: releases
|
||||||
|
@ -8,7 +8,7 @@ include(EthPolicy)
|
|||||||
eth_policy()
|
eth_policy()
|
||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.5.1")
|
set(PROJECT_VERSION "0.5.2")
|
||||||
project(solidity VERSION ${PROJECT_VERSION})
|
project(solidity VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
option(LLL "Build LLL" OFF)
|
option(LLL "Build LLL" OFF)
|
||||||
|
29
Changelog.md
29
Changelog.md
@ -1,3 +1,32 @@
|
|||||||
|
### 0.5.2 (2018-12-19)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Control Flow Graph: Detect every access to uninitialized storage pointers.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
* Inline Assembly: Improve error messages around invalid function argument count.
|
||||||
|
* Code Generator: Only check callvalue once if all functions are non-payable.
|
||||||
|
* Code Generator: Use codecopy for string constants more aggressively.
|
||||||
|
* Code Generator: Use binary search for dispatch function if more efficient. The size/speed tradeoff can be tuned using ``--optimize-runs``.
|
||||||
|
* SMTChecker: Support mathematical and cryptographic functions in an uninterpreted way.
|
||||||
|
* SMTChecker: Support one-dimensional mappings.
|
||||||
|
* Standard JSON Interface: Disallow unknown keys in standard JSON input.
|
||||||
|
* Standard JSON Interface: Only run code generation if it has been requested. This could lead to unsupported feature errors only being reported at the point where you request bytecode.
|
||||||
|
* Static Analyzer: Do not warn about unused variables or state mutability for functions with an empty body.
|
||||||
|
* Type Checker: Add an additional reason to be displayed when type conversion fails.
|
||||||
|
* Yul: Support object access via ``datasize``, ``dataoffset`` and ``datacopy`` in standalone assembly mode.
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Standard JSON Interface: Report specific error message for json input errors instead of internal compiler error.
|
||||||
|
|
||||||
|
|
||||||
|
Build System:
|
||||||
|
* Replace the trusty PPA build by a static build on cosmic that is used for the trusty package instead.
|
||||||
|
* Remove support for Visual Studio 2015.
|
||||||
|
|
||||||
|
|
||||||
### 0.5.1 (2018-12-03)
|
### 0.5.1 (2018-12-03)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
@ -10,6 +10,7 @@ Checklist for making a release:
|
|||||||
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
|
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
|
||||||
- [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page).
|
- [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page).
|
||||||
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
|
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
|
||||||
|
- [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` while selecting ``Copy existing binaries``.
|
||||||
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``).
|
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``).
|
||||||
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
|
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
|
||||||
- [ ] Update the default version on readthedocs.
|
- [ ] Update the default version on readthedocs.
|
||||||
|
@ -32,10 +32,9 @@ branches:
|
|||||||
- release
|
- release
|
||||||
- develop
|
- develop
|
||||||
configuration:
|
configuration:
|
||||||
- RelWithDebInfo
|
- Release
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
# This is used for pushing to solidity-test-bytecodes
|
# This is used for pushing to solidity-test-bytecodes
|
||||||
priv_key:
|
priv_key:
|
||||||
@ -63,12 +62,11 @@ install:
|
|||||||
before_build:
|
before_build:
|
||||||
- if not exist build mkdir build
|
- if not exist build mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On )
|
- cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On
|
||||||
else ( cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On )
|
|
||||||
build_script:
|
build_script:
|
||||||
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
|
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
- cd %APPVEYOR_BUILD_FOLDER%
|
||||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" ( scripts\release.bat %CONFIGURATION% 2017 )
|
- scripts\release.bat %CONFIGURATION% 2017
|
||||||
- ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
|
- ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
|
@ -25,8 +25,8 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
|||||||
|
|
||||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||||
|
|
||||||
# Use ISO C++11 standard language.
|
# Use ISO C++14 standard language.
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||||
|
|
||||||
# Enables all the warnings about constructions that some users consider questionable,
|
# Enables all the warnings about constructions that some users consider questionable,
|
||||||
# and that are easy to avoid. Also enable some extra warning flags that are not
|
# and that are easy to avoid. Also enable some extra warning flags that are not
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
find_library(CLN_LIBRARY NAMES cln)
|
find_library(CLN_LIBRARY NAMES cln)
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY)
|
find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY)
|
||||||
|
|
||||||
|
if(CLN_FOUND AND NOT TARGET CLN::CLN)
|
||||||
|
add_library(CLN::CLN UNKNOWN IMPORTED)
|
||||||
|
set_property(TARGET CLN::CLN PROPERTY IMPORTED_LOCATION ${CLN_LIBRARY})
|
||||||
|
endif()
|
||||||
|
@ -14,11 +14,18 @@ if (USE_CVC4)
|
|||||||
set(CVC4_LIBRARIES ${CVC4_LIBRARY})
|
set(CVC4_LIBRARIES ${CVC4_LIBRARY})
|
||||||
|
|
||||||
if (CLN_FOUND)
|
if (CLN_FOUND)
|
||||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${CLN_LIBRARY})
|
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} CLN::CLN)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (GMP_FOUND)
|
if (GMP_FOUND)
|
||||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${GMP_LIBRARY})
|
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} GMP::GMP)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT TARGET CVC4::CVC4)
|
||||||
|
add_library(CVC4::CVC4 UNKNOWN IMPORTED)
|
||||||
|
set_property(TARGET CVC4::CVC4 PROPERTY IMPORTED_LOCATION ${CVC4_LIBRARY})
|
||||||
|
set_property(TARGET CVC4::CVC4 PROPERTY INTERFACE_LINK_LIBRARIES ${CVC4_LIBRARIES})
|
||||||
|
set_property(TARGET CVC4::CVC4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CVC4_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
find_library(GMP_LIBRARY NAMES gmp)
|
find_library(GMP_LIBRARY NAMES gmp)
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY)
|
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY)
|
||||||
|
|
||||||
|
if(GMP_FOUND AND NOT TARGET GMP::GMP)
|
||||||
|
add_library(GMP::GMP UNKNOWN IMPORTED)
|
||||||
|
set_property(TARGET GMP::GMP PROPERTY IMPORTED_LOCATION ${GMP_LIBRARY})
|
||||||
|
endif()
|
||||||
|
@ -3,7 +3,12 @@ if (USE_Z3)
|
|||||||
find_library(Z3_LIBRARY NAMES z3)
|
find_library(Z3_LIBRARY NAMES z3)
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
|
find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if (NOT TARGET Z3::Z3)
|
||||||
|
add_library(Z3::Z3 UNKNOWN IMPORTED)
|
||||||
|
set_property(TARGET Z3::Z3 PROPERTY IMPORTED_LOCATION ${Z3_LIBRARY})
|
||||||
|
set_property(TARGET Z3::Z3 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Z3_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
set(Z3_FOUND FALSE)
|
set(Z3_FOUND FALSE)
|
||||||
endif()
|
endif()
|
||||||
# TODO: Create IMPORTED library for Z3.
|
|
||||||
|
@ -15,9 +15,9 @@ set(JSONCPP_INCLUDE_DIR "${prefix}/include")
|
|||||||
# versions used in the CI runs.
|
# versions used in the CI runs.
|
||||||
if(EMSCRIPTEN)
|
if(EMSCRIPTEN)
|
||||||
# Do not include all flags in CMAKE_CXX_FLAGS for emscripten,
|
# Do not include all flags in CMAKE_CXX_FLAGS for emscripten,
|
||||||
# but only use -std=c++11. Using all flags causes build failures
|
# but only use -std=c++14. Using all flags causes build failures
|
||||||
# at the moment.
|
# at the moment.
|
||||||
set(JSONCPP_CXX_FLAGS -std=c++11)
|
set(JSONCPP_CXX_FLAGS -std=c++14)
|
||||||
else()
|
else()
|
||||||
set(JSONCPP_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
set(JSONCPP_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
endif()
|
endif()
|
||||||
@ -51,5 +51,6 @@ ExternalProject_Add(jsoncpp-project
|
|||||||
add_library(jsoncpp STATIC IMPORTED)
|
add_library(jsoncpp STATIC IMPORTED)
|
||||||
file(MAKE_DIRECTORY ${JSONCPP_INCLUDE_DIR}) # Must exist.
|
file(MAKE_DIRECTORY ${JSONCPP_INCLUDE_DIR}) # Must exist.
|
||||||
set_property(TARGET jsoncpp PROPERTY IMPORTED_LOCATION ${JSONCPP_LIBRARY})
|
set_property(TARGET jsoncpp PROPERTY IMPORTED_LOCATION ${JSONCPP_LIBRARY})
|
||||||
|
set_property(TARGET jsoncpp PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR})
|
||||||
set_property(TARGET jsoncpp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR})
|
set_property(TARGET jsoncpp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR})
|
||||||
add_dependencies(jsoncpp jsoncpp-project)
|
add_dependencies(jsoncpp jsoncpp-project)
|
||||||
|
@ -159,8 +159,8 @@ 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.
|
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).
|
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
|
Opcodes marked with ``-`` do not push an item onto the stack (do not return a result),
|
||||||
special and all others push exactly one item onto the stack.
|
those marked with ``*`` are special and all others push exactly one item onto the stack (their "return value").
|
||||||
Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively.
|
Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively.
|
||||||
Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
|
Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ Literals
|
|||||||
|
|
||||||
You can use integer constants by typing them in decimal or hexadecimal notation and an
|
You can use integer constants by typing them in decimal or hexadecimal notation and an
|
||||||
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
|
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
|
||||||
to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc".
|
to add 2 and 3 resulting in 5 and then computes the bitwise ``AND`` with the string "abc".
|
||||||
The final value is assigned to a local variable called ``x``.
|
The final value is assigned to a local variable called ``x``.
|
||||||
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
||||||
|
|
||||||
|
@ -616,5 +616,9 @@
|
|||||||
"0.5.1": {
|
"0.5.1": {
|
||||||
"bugs": [],
|
"bugs": [],
|
||||||
"released": "2018-12-03"
|
"released": "2018-12-03"
|
||||||
|
},
|
||||||
|
"0.5.2": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2018-12-19"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1408,7 +1408,7 @@ Interfaces are denoted by their own keyword:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity >=0.4.11 <0.6.0;
|
pragma solidity >=0.5.0 <0.6.0;
|
||||||
|
|
||||||
interface Token {
|
interface Token {
|
||||||
enum TokenType { Fungible, NonFungible }
|
enum TokenType { Fungible, NonFungible }
|
||||||
|
@ -61,7 +61,7 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please
|
|||||||
follow the style of previous entries, when applicable.
|
follow the style of previous entries, when applicable.
|
||||||
|
|
||||||
Finally, please make sure you respect the `coding style
|
Finally, please make sure you respect the `coding style
|
||||||
<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_
|
<https://github.com/ethereum/solidity/blob/develop/CODING_STYLE.md>`_
|
||||||
for this project. Also, even though we do CI testing, please test your code and
|
for this project. Also, even though we do CI testing, please test your code and
|
||||||
ensure that it builds locally before submitting a pull request.
|
ensure that it builds locally before submitting a pull request.
|
||||||
|
|
||||||
@ -83,14 +83,14 @@ internally.
|
|||||||
|
|
||||||
.. note ::
|
.. note ::
|
||||||
|
|
||||||
Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/RelWithDebInfo/soltest.exe -- --no-ipc --no-smt``.
|
Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/Release/soltest.exe -- --no-ipc --no-smt``.
|
||||||
If you're running this in plain Command Prompt, use ``.\build\test\RelWithDebInfo\soltest.exe -- --no-ipc --no-smt``.
|
If you're running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-ipc --no-smt``.
|
||||||
|
|
||||||
The option ``--no-smt`` disables the tests that require ``libz3`` and
|
The option ``--no-smt`` disables the tests that require ``libz3`` and
|
||||||
``--no-ipc`` disables those that require ``aleth``.
|
``--no-ipc`` disables those that require ``aleth``.
|
||||||
|
|
||||||
If you want to run the ipc tests (that test the semantics of the generated code),
|
If you want to run the ipc tests (that test the semantics of the generated code),
|
||||||
you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it).
|
you need to install `aleth <https://github.com/ethereum/aleth/releases/download/v1.5.0-alpha.7/aleth-1.5.0-alpha.7-linux-x86_64.tar.gz>`_ and run it in testing mode: ``aleth --db memorydb --test -d /tmp/testeth``.
|
||||||
|
|
||||||
To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
|
To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
|
||||||
|
|
||||||
@ -122,9 +122,9 @@ The CI runs additional tests (including ``solc-js`` and testing third party Soli
|
|||||||
|
|
||||||
.. note ::
|
.. note ::
|
||||||
|
|
||||||
You can not use some versions of ``aleth`` for testing. We suggest using
|
Some versions of ``aleth`` can not be used for testing. We suggest using
|
||||||
the same version that the Solidity continuous integration tests use.
|
the same version that the Solidity continuous integration tests use.
|
||||||
Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``.
|
Currently the CI uses version ``1.5.0-alpha.7`` of ``aleth``.
|
||||||
|
|
||||||
Writing and running syntax tests
|
Writing and running syntax tests
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
@ -9,35 +9,6 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_.
|
|||||||
Basic Questions
|
Basic Questions
|
||||||
***************
|
***************
|
||||||
|
|
||||||
What is the transaction "payload"?
|
|
||||||
==================================
|
|
||||||
|
|
||||||
This is just the bytecode "data" sent along with the request.
|
|
||||||
|
|
||||||
|
|
||||||
Create a contract that can be killed and return funds
|
|
||||||
=====================================================
|
|
||||||
|
|
||||||
First, a word of warning: Killing contracts sounds like a good idea, because "cleaning up"
|
|
||||||
is always good, but as seen above, it does not really clean up. Furthermore,
|
|
||||||
if Ether is sent to removed contracts, the Ether will be forever lost.
|
|
||||||
|
|
||||||
If you want to deactivate your contracts, it is preferable to **disable** them by changing some
|
|
||||||
internal state which causes all functions to throw. This will make it impossible
|
|
||||||
to use the contract and ether sent to the contract will be returned automatically.
|
|
||||||
|
|
||||||
Now to answering the question: Inside a constructor, ``msg.sender`` is the
|
|
||||||
creator. Save it. Then ``selfdestruct(creator);`` to kill and return funds.
|
|
||||||
|
|
||||||
`example <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_
|
|
||||||
|
|
||||||
Note that if you ``import "mortal"`` at the top of your contracts and declare
|
|
||||||
``contract SomeContract is mortal { ...`` and compile with a compiler that already
|
|
||||||
has it (which includes `Remix <https://remix.ethereum.org/>`_), then
|
|
||||||
``kill()`` is taken care of for you. Once a contract is "mortal", then you can
|
|
||||||
``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
|
|
||||||
examples.
|
|
||||||
|
|
||||||
If I return an ``enum``, I only get integer values in web3.js. How to get the named values?
|
If I return an ``enum``, I only get integer values in web3.js. How to get the named values?
|
||||||
===========================================================================================
|
===========================================================================================
|
||||||
|
|
||||||
@ -45,31 +16,6 @@ Enums are not supported by the ABI, they are just supported by Solidity.
|
|||||||
You have to do the mapping yourself for now, we might provide some help
|
You have to do the mapping yourself for now, we might provide some help
|
||||||
later.
|
later.
|
||||||
|
|
||||||
Can state variables be initialized in-line?
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
Yes, this is possible for all types (even for structs). However, for arrays it
|
|
||||||
should be noted that you must declare them as static memory arrays.
|
|
||||||
|
|
||||||
Examples::
|
|
||||||
|
|
||||||
pragma solidity >=0.4.0 <0.6.0;
|
|
||||||
|
|
||||||
contract C {
|
|
||||||
struct S {
|
|
||||||
uint a;
|
|
||||||
uint b;
|
|
||||||
}
|
|
||||||
|
|
||||||
S public x = S(1, 2);
|
|
||||||
string name = "Ada";
|
|
||||||
string[4] adaArr = ["This", "is", "an", "array"];
|
|
||||||
}
|
|
||||||
|
|
||||||
contract D {
|
|
||||||
C c = new C();
|
|
||||||
}
|
|
||||||
|
|
||||||
How do structs work?
|
How do structs work?
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
@ -296,13 +296,13 @@ And for Windows:
|
|||||||
This latter set of instructions should result in the creation of
|
This latter set of instructions should result in the creation of
|
||||||
**solidity.sln** in that build directory. Double-clicking on that file
|
**solidity.sln** in that build directory. Double-clicking on that file
|
||||||
should result in Visual Studio firing up. We suggest building
|
should result in Visual Studio firing up. We suggest building
|
||||||
**RelWithDebugInfo** configuration, but all others work.
|
**Release** configuration, but all others work.
|
||||||
|
|
||||||
Alternatively, you can build for Windows on the command-line, like so:
|
Alternatively, you can build for Windows on the command-line, like so:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
cmake --build . --config RelWithDebInfo
|
cmake --build . --config Release
|
||||||
|
|
||||||
CMake options
|
CMake options
|
||||||
=============
|
=============
|
||||||
|
@ -41,7 +41,7 @@ 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
|
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
|
data (its *state*) that resides at a specific address on the Ethereum
|
||||||
blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of
|
blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of
|
||||||
type ``uint`` (*u*nsigned *int*eger of *256* bits). You can think of it as a single slot
|
type ``uint`` (*u*\nsigned *int*\eger of *256* bits). You can think of it as a single slot
|
||||||
in a database that can be queried and altered by calling functions of the
|
in a database that can be queried and altered by calling functions of the
|
||||||
code that manages the database. In the case of Ethereum, this is always the owning
|
code that manages the database. In the case of Ethereum, this is always the owning
|
||||||
contract. And in this case, the functions ``set`` and ``get`` can be used to modify
|
contract. And in this case, the functions ``set`` and ``get`` can be used to modify
|
||||||
|
@ -267,7 +267,7 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible
|
|||||||
(these are NEL, LS and PS), it will lead to a parser error.
|
(these are NEL, LS and PS), it will lead to a parser error.
|
||||||
|
|
||||||
Additionally, there is another type of comment called a natspec comment,
|
Additionally, there is another type of comment called a natspec comment,
|
||||||
for which the documentation is not yet written. They are written with a
|
which is detailed in the :ref:`style guide<natspec>`. They are written with a
|
||||||
triple slash (``///``) or a double asterisk block(``/** ... */``) and
|
triple slash (``///``) or a double asterisk block(``/** ... */``) and
|
||||||
they should be used directly above function declarations or statements.
|
they should be used directly above function declarations or statements.
|
||||||
You can use `Doxygen <https://en.wikipedia.org/wiki/Doxygen>`_-style tags inside these comments to document
|
You can use `Doxygen <https://en.wikipedia.org/wiki/Doxygen>`_-style tags inside these comments to document
|
||||||
|
@ -224,9 +224,9 @@ for displaying the current position in the source code inside a debugger
|
|||||||
or for breakpoint handling.
|
or for breakpoint handling.
|
||||||
|
|
||||||
Both kinds of source mappings use integer identifiers to refer to source files.
|
Both kinds of source mappings use integer identifiers to refer to source files.
|
||||||
These are regular array indices into a list of source files usually called
|
The identifier of a source file is stored in
|
||||||
``"sourceList"``, which is part of the combined-json and the output of
|
``output['sources'][sourceName]['id']`` where ``output`` is the output of the
|
||||||
the json / npm compiler.
|
standard-json compiler interface parsed as JSON.
|
||||||
|
|
||||||
.. note ::
|
.. note ::
|
||||||
In the case of instructions that are not associated with any particular source file,
|
In the case of instructions that are not associated with any particular source file,
|
||||||
|
@ -743,7 +743,7 @@ In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatu
|
|||||||
Extracting the Signature Parameters
|
Extracting the Signature Parameters
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a messy, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
|
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a mess, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
|
||||||
|
|
||||||
Computing the Message Hash
|
Computing the Message Hash
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -1093,3 +1093,64 @@ General Recommendations
|
|||||||
=======================
|
=======================
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
|
.. _natspec:
|
||||||
|
|
||||||
|
*******
|
||||||
|
NatSpec
|
||||||
|
*******
|
||||||
|
|
||||||
|
Solidity contracts can have a form of comments that are the basis of the
|
||||||
|
Ethereum Natural Language Specification Format.
|
||||||
|
|
||||||
|
Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation
|
||||||
|
of one or multiple lines starting with `///` or a
|
||||||
|
multiline comment starting with `/**` and ending with `*/`.
|
||||||
|
|
||||||
|
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
|
||||||
|
added looks like the one below::
|
||||||
|
|
||||||
|
pragma solidity >=0.4.0 <0.6.0;
|
||||||
|
|
||||||
|
/// @author The Solidity Team
|
||||||
|
/// @title A simple storage example
|
||||||
|
contract SimpleStorage {
|
||||||
|
uint storedData;
|
||||||
|
|
||||||
|
/// Store `x`.
|
||||||
|
/// @param x the new value to store
|
||||||
|
/// @dev stores the number in the state variable `storedData`
|
||||||
|
function set(uint x) public {
|
||||||
|
storedData = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the stored value.
|
||||||
|
/// @dev retrieves the value of the state variable `storedData`
|
||||||
|
/// @return the stored value
|
||||||
|
function get() public view returns (uint) {
|
||||||
|
return storedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Natspec uses doxygen style tags with some special meaning.
|
||||||
|
If no tag is used, then the comment applies to ``@notice``.
|
||||||
|
The ``@notice`` tag is the main NatSpec tag and its audience is
|
||||||
|
users of the contract who have never seen the source code, so it should make
|
||||||
|
as little assumptions about the inner details as possible.
|
||||||
|
All tags are optional.
|
||||||
|
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| Tag | Description | Context |
|
||||||
|
+=============+===========================================+===============================+
|
||||||
|
| ``@title`` | A title that describes the contract | contract, interface |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| ``@author`` | The name of the author | contract, interface, function |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| ``@notice`` | Explanation of functionality | contract, interface, function |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| ``@dev`` | Any extra details | contract, interface, function |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| ``@param`` | Parameter type followed by parameter name | function |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
| ``@return`` | The return value of a contract's function | function |
|
||||||
|
+-------------+-------------------------------------------+-------------------------------+
|
||||||
|
@ -19,6 +19,7 @@ on its type. To handle any unexpected values, you should use the :ref:`revert fu
|
|||||||
tuple with a second `bool` value denoting success.
|
tuple with a second `bool` value denoting success.
|
||||||
|
|
||||||
.. index:: ! value type, ! type;value
|
.. index:: ! value type, ! type;value
|
||||||
|
.. _value-types:
|
||||||
|
|
||||||
Value Types
|
Value Types
|
||||||
===========
|
===========
|
||||||
@ -728,6 +729,8 @@ Another example that uses external function types::
|
|||||||
|
|
||||||
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
|
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
|
||||||
|
|
||||||
|
.. _reference-types:
|
||||||
|
|
||||||
Reference Types
|
Reference Types
|
||||||
===============
|
===============
|
||||||
|
|
||||||
@ -761,14 +764,17 @@ non-persistent area where function arguments are stored, and behaves mostly like
|
|||||||
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
||||||
data location.
|
data location.
|
||||||
|
|
||||||
|
.. _data-location-assignment:
|
||||||
|
|
||||||
|
Data location and assignment behaviour
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
|
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
|
||||||
assignments between storage and memory (or from calldata) always create an independent copy.
|
|
||||||
Assignments from memory to memory only create references. This means that changes to one memory variable
|
* Assignments between ``storage`` and ``memory`` (or from ``calldata``) always create an independent copy.
|
||||||
are also visible in all other memory variables that refer to the same data.
|
* Assignments from ``memory`` to ``memory`` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.
|
||||||
Assignments from storage to a local storage variables also only assign a reference.
|
* Assignments from ``storage`` to a local storage variable also only assign a reference.
|
||||||
In contrast, all other assignments to storage always copy. Examples for this case
|
* All other assignments to ``storage`` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.
|
||||||
are assignments to state variables or to members of local variables of storage struct type, even
|
|
||||||
if the local variable itself is just a reference.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -805,40 +811,50 @@ if the local variable itself is just a reference.
|
|||||||
Arrays
|
Arrays
|
||||||
------
|
------
|
||||||
|
|
||||||
Arrays can have a compile-time fixed size or they can be dynamic.
|
Arrays can have a compile-time fixed size, or they can have a dynamic size.
|
||||||
The are few restrictions for the element, it can also be
|
|
||||||
another array, a mapping or a struct. The general restrictions for
|
|
||||||
types apply, though, in that mappings can only be used in storage
|
|
||||||
and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`.
|
|
||||||
|
|
||||||
An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
|
The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
|
||||||
an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic
|
and an array of dynamic size as ``T[]``.
|
||||||
arrays of ``uint`` is ``uint[][5]`` (note that the notation is reversed when
|
|
||||||
compared to some other languages). To access the second uint in the
|
|
||||||
third dynamic array, you use ``x[2][1]`` (indices are zero-based and
|
|
||||||
access works in the opposite way of the declaration, i.e. ``x[2]``
|
|
||||||
shaves off one level in the type from the right).
|
|
||||||
|
|
||||||
Accessing an array past its end causes a revert. If you want to add
|
For example, an array of 5 dynamic arrays of ``uint`` is written as
|
||||||
new elements, you have to use ``.push()`` or increase the ``.length``
|
``uint[][5]``. The notation is reversed compared to some other languages. In
|
||||||
member (see below).
|
Solidity, ``X[3]`` is always an array containing three elements of type ``X``,
|
||||||
|
even if ``X`` is itself an array. This is not the case in other languages such
|
||||||
|
as C.
|
||||||
|
|
||||||
|
Indices are zero-based, and access is in the opposite direction of the
|
||||||
|
declaration.
|
||||||
|
|
||||||
|
For example, if you have a variable ``uint[][5] x memory``, you access the
|
||||||
|
second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the
|
||||||
|
third dynamic array, use ``x[2]``. Again,
|
||||||
|
if you have an array ``T[5] a`` for a type ``T`` that can also be an array,
|
||||||
|
then ``a[2]`` always has type ``T``.
|
||||||
|
|
||||||
|
Array elements can be of any type, including mapping or struct. The general
|
||||||
|
restrictions for types apply, in that mappings can only be stored in the
|
||||||
|
``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`.
|
||||||
|
|
||||||
|
Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member <array-members>` to change the size (see below for caveats).
|
||||||
|
method or increase the ``.length`` :ref:`member <array-members>` to add elements.
|
||||||
|
|
||||||
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
|
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
|
||||||
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
|
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
|
||||||
length or index access.
|
length or index access.
|
||||||
So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper.
|
|
||||||
As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string``
|
You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule,
|
||||||
for arbitrary-length string (UTF-8) data. If you can limit the length to a certain
|
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
|
||||||
number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper.
|
string (UTF-8) data. If you can limit the length to a certain number of bytes,
|
||||||
|
always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If you want to access the byte-representation of a string ``s``, use
|
If you want to access the byte-representation of a string ``s``, use
|
||||||
``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind
|
``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind
|
||||||
that you are accessing the low-level bytes of the UTF-8 representation,
|
that you are accessing the low-level bytes of the UTF-8 representation,
|
||||||
and not the individual characters!
|
and not the individual characters.
|
||||||
|
|
||||||
It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`.
|
It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`.
|
||||||
The numeric index will become a required parameter for the getter.
|
The numeric index becomes a required parameter for the getter.
|
||||||
|
|
||||||
.. index:: ! array;allocating, new
|
.. index:: ! array;allocating, new
|
||||||
|
|
||||||
@ -912,8 +928,10 @@ complications because of how arrays are passed in the ABI.
|
|||||||
|
|
||||||
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
|
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
|
||||||
|
|
||||||
Members
|
.. _array-members:
|
||||||
^^^^^^^
|
|
||||||
|
Array Members
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
**length**:
|
**length**:
|
||||||
Arrays have a ``length`` member that contains their number of elements.
|
Arrays have a ``length`` member that contains their number of elements.
|
||||||
@ -1120,9 +1138,10 @@ assigning it to a local variable, as in
|
|||||||
``campaigns[campaignID].amount = 0``.
|
``campaigns[campaignID].amount = 0``.
|
||||||
|
|
||||||
.. index:: !mapping
|
.. index:: !mapping
|
||||||
|
.. _mapping-types:
|
||||||
|
|
||||||
Mappings
|
Mapping Types
|
||||||
--------
|
=============
|
||||||
|
|
||||||
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
||||||
The ``_KeyType`` can be any elementary type. This means it can be any of
|
The ``_KeyType`` can be any elementary type. This means it can be any of
|
||||||
@ -1189,7 +1208,14 @@ If ``a`` is an LValue (i.e. a variable or something that can be assigned to), th
|
|||||||
delete
|
delete
|
||||||
------
|
------
|
||||||
|
|
||||||
``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
|
``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is
|
||||||
|
equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic
|
||||||
|
array of length zero or a static array of the same length with all elements set to their
|
||||||
|
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
|
||||||
|
all other elements and the length of the array untouched. This especially means that it leaves
|
||||||
|
a gap in the array. If you plan to remove items, a mapping is probably a better choice.
|
||||||
|
|
||||||
|
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
|
||||||
|
|
||||||
``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``.
|
``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``.
|
||||||
|
|
||||||
|
@ -200,15 +200,27 @@ Input Description
|
|||||||
"MyLib": "0x123123..."
|
"MyLib": "0x123123..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The following can be used to select desired outputs.
|
// The following can be used to select desired outputs based
|
||||||
// If this field is omitted, then the compiler loads and does type checking, but will not generate any outputs apart from errors.
|
// on file and contract names.
|
||||||
// The first level key is the file name and the second is the contract name, where empty contract name refers to the file itself,
|
// If this field is omitted, then the compiler loads and does type checking,
|
||||||
// while the star refers to all of the contracts.
|
// but will not generate any outputs apart from errors.
|
||||||
|
// The first level key is the file name and the second level key is the contract name.
|
||||||
|
// An empty contract name is used for outputs that are not tied to a contract
|
||||||
|
// but to the whole source file like the AST.
|
||||||
|
// A star as contract name refers to all contracts in the file.
|
||||||
|
// Similarly, a star as a file name matches all files.
|
||||||
|
// To select all outputs the compiler can possibly generate, use
|
||||||
|
// "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }"
|
||||||
|
// but note that this might slow down the compilation process needlessly.
|
||||||
//
|
//
|
||||||
// The available output types are as follows:
|
// The available output types are as follows:
|
||||||
// abi - ABI
|
//
|
||||||
|
// File level (needs empty string as contract name):
|
||||||
// ast - AST of all source files
|
// ast - AST of all source files
|
||||||
// legacyAST - legacy AST of all source files
|
// legacyAST - legacy AST of all source files
|
||||||
|
//
|
||||||
|
// Contract level (needs the contract name or "*"):
|
||||||
|
// abi - ABI
|
||||||
// devdoc - Developer documentation (natspec)
|
// devdoc - Developer documentation (natspec)
|
||||||
// userdoc - User documentation (natspec)
|
// userdoc - User documentation (natspec)
|
||||||
// metadata - Metadata
|
// metadata - Metadata
|
||||||
@ -281,7 +293,7 @@ Output Description
|
|||||||
// This contains the file-level outputs. In can be limited/filtered by the outputSelection settings.
|
// This contains the file-level outputs. In can be limited/filtered by the outputSelection settings.
|
||||||
sources: {
|
sources: {
|
||||||
"sourceFile.sol": {
|
"sourceFile.sol": {
|
||||||
// Identifier (used in source maps)
|
// Identifier of the source (used in source maps)
|
||||||
id: 1,
|
id: 1,
|
||||||
// The AST object
|
// The AST object
|
||||||
ast: {},
|
ast: {},
|
||||||
|
@ -1,18 +1,36 @@
|
|||||||
set(sources
|
set(sources
|
||||||
|
Algorithms.h
|
||||||
|
Assertions.h
|
||||||
|
boost_multiprecision_number_compare_bug_workaround.hpp
|
||||||
|
Common.h
|
||||||
CommonData.cpp
|
CommonData.cpp
|
||||||
|
CommonData.h
|
||||||
CommonIO.cpp
|
CommonIO.cpp
|
||||||
|
CommonIO.h
|
||||||
Exceptions.cpp
|
Exceptions.cpp
|
||||||
|
Exceptions.h
|
||||||
|
FixedHash.h
|
||||||
IndentedWriter.cpp
|
IndentedWriter.cpp
|
||||||
|
IndentedWriter.h
|
||||||
JSON.cpp
|
JSON.cpp
|
||||||
|
JSON.h
|
||||||
Keccak256.cpp
|
Keccak256.cpp
|
||||||
|
Keccak256.h
|
||||||
|
Result.h
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
|
StringUtils.h
|
||||||
SwarmHash.cpp
|
SwarmHash.cpp
|
||||||
|
SwarmHash.h
|
||||||
UTF8.cpp
|
UTF8.cpp
|
||||||
|
UTF8.h
|
||||||
|
vector_ref.h
|
||||||
|
Visitor.h
|
||||||
Whiskers.cpp
|
Whiskers.cpp
|
||||||
|
Whiskers.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(devcore ${sources})
|
add_library(devcore ${sources})
|
||||||
target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(devcore PUBLIC jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} Threads::Threads)
|
||||||
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||||
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||||
add_dependencies(devcore solidity_BuildInfo.h)
|
add_dependencies(devcore solidity_BuildInfo.h)
|
||||||
|
@ -29,6 +29,28 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
|
|
||||||
|
string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
|
||||||
|
{
|
||||||
|
std::ostringstream ret;
|
||||||
|
if (_prefix == HexPrefix::Add)
|
||||||
|
ret << "0x";
|
||||||
|
|
||||||
|
int rix = _data.size() - 1;
|
||||||
|
for (uint8_t c: _data)
|
||||||
|
{
|
||||||
|
// switch hex case every four hexchars
|
||||||
|
auto hexcase = std::nouppercase;
|
||||||
|
if (_case == HexCase::Upper)
|
||||||
|
hexcase = std::uppercase;
|
||||||
|
else if (_case == HexCase::Mixed)
|
||||||
|
hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase;
|
||||||
|
|
||||||
|
ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.str();
|
||||||
|
}
|
||||||
|
|
||||||
int dev::fromHex(char _i, WhenError _throw)
|
int dev::fromHex(char _i, WhenError _throw)
|
||||||
{
|
{
|
||||||
if (_i >= '0' && _i <= '9')
|
if (_i >= '0' && _i <= '9')
|
||||||
|
@ -50,18 +50,18 @@ enum class HexPrefix
|
|||||||
DontAdd = 0,
|
DontAdd = 0,
|
||||||
Add = 1,
|
Add = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HexCase
|
||||||
|
{
|
||||||
|
Lower = 0,
|
||||||
|
Upper = 1,
|
||||||
|
Mixed = 2,
|
||||||
|
};
|
||||||
|
|
||||||
/// Convert a series of bytes to the corresponding string of hex duplets.
|
/// Convert a series of bytes to the corresponding string of hex duplets.
|
||||||
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
|
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
|
||||||
/// @example toHex("A\x69") == "4169"
|
/// @example toHex("A\x69") == "4169"
|
||||||
template <class T>
|
std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower);
|
||||||
std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd)
|
|
||||||
{
|
|
||||||
std::ostringstream ret;
|
|
||||||
unsigned ii = 0;
|
|
||||||
for (auto i: _data)
|
|
||||||
ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned<decltype(i)>::type)i;
|
|
||||||
return (_prefix == HexPrefix::Add) ? "0x" + ret.str() : ret.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a (printable) ASCII hex character into the correspnding integer value.
|
/// Converts a (printable) ASCII hex character into the correspnding integer value.
|
||||||
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
|
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
|
||||||
@ -153,7 +153,7 @@ inline std::string formatNumber(bigint const& _value)
|
|||||||
if (_value < 0)
|
if (_value < 0)
|
||||||
return "-" + formatNumber(-_value);
|
return "-" + formatNumber(-_value);
|
||||||
if (_value > 0x1000000)
|
if (_value > 0x1000000)
|
||||||
return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
|
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
|
||||||
else
|
else
|
||||||
return _value.str();
|
return _value.str();
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ inline std::string formatNumber(bigint const& _value)
|
|||||||
inline std::string formatNumber(u256 const& _value)
|
inline std::string formatNumber(u256 const& _value)
|
||||||
{
|
{
|
||||||
if (_value > 0x1000000)
|
if (_value > 0x1000000)
|
||||||
return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
|
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
|
||||||
else
|
else
|
||||||
return _value.str();
|
return _value.str();
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ public:
|
|||||||
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
|
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
|
||||||
|
|
||||||
/// @returns the hash as a user-readable hex string.
|
/// @returns the hash as a user-readable hex string.
|
||||||
std::string hex() const { return toHex(ref()); }
|
std::string hex() const { return toHex(asBytes()); }
|
||||||
|
|
||||||
/// @returns a mutable byte vector_ref to the object's data.
|
/// @returns a mutable byte vector_ref to the object's data.
|
||||||
bytesRef ref() { return bytesRef(m_data.data(), N); }
|
bytesRef ref() { return bytesRef(m_data.data(), N); }
|
||||||
|
@ -36,7 +36,7 @@ string IndentedWriter::format() const
|
|||||||
void IndentedWriter::newLine()
|
void IndentedWriter::newLine()
|
||||||
{
|
{
|
||||||
if (!m_lines.back().contents.empty())
|
if (!m_lines.back().contents.empty())
|
||||||
m_lines.push_back({ string(), m_lines.back().indentation });
|
m_lines.emplace_back(Line{string(), m_lines.back().indentation});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndentedWriter::indent()
|
void IndentedWriter::indent()
|
||||||
|
@ -34,8 +34,6 @@ DEV_SIMPLE_EXCEPTION(IndentedWriterError);
|
|||||||
class IndentedWriter
|
class IndentedWriter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit IndentedWriter(): m_lines(std::vector<Line>{{std::string(), 0}}) {}
|
|
||||||
|
|
||||||
// Returns the formatted output.
|
// Returns the formatted output.
|
||||||
std::string format() const;
|
std::string format() const;
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ private:
|
|||||||
unsigned indentation;
|
unsigned indentation;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Line> m_lines;
|
std::vector<Line> m_lines{{std::string(), 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
66
libdevcore/Result.h
Normal file
66
libdevcore/Result.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Simple generic result that holds a value and an optional error message.
|
||||||
|
/// Results can be implicitly converted to and created from the type of
|
||||||
|
/// the value they hold. The class is mainly designed for a result type of
|
||||||
|
/// bool or pointer type. The idea is that the default constructed value of
|
||||||
|
/// the result type is interpreted as an error value.
|
||||||
|
///
|
||||||
|
/// Result<bool> check()
|
||||||
|
/// {
|
||||||
|
/// if (false)
|
||||||
|
/// return Result<bool>("Error message.")
|
||||||
|
/// return true;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
|
||||||
|
template <class ResultType>
|
||||||
|
class Result
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Result(ResultType _value): Result(_value, std::string{}) {}
|
||||||
|
Result(std::string _message): Result(ResultType{}, std::move(_message)) {}
|
||||||
|
|
||||||
|
/// @{
|
||||||
|
/// @name Wrapper functions
|
||||||
|
/// Wrapper functions that provide implicit conversions to and explicit retrieval of
|
||||||
|
/// the value this result holds.
|
||||||
|
operator ResultType const&() const { return m_value; }
|
||||||
|
ResultType const& get() const { return m_value; }
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @returns the error message (can be empty).
|
||||||
|
std::string const& message() const { return m_message; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit Result(ResultType _value, std::string _message):
|
||||||
|
m_value(std::move(_value)),
|
||||||
|
m_message(std::move(_message))
|
||||||
|
{}
|
||||||
|
|
||||||
|
ResultType m_value;
|
||||||
|
std::string m_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -91,7 +91,7 @@ string dev::quotedAlternativesList(vector<string> const& suggestions)
|
|||||||
vector<string> quotedSuggestions;
|
vector<string> quotedSuggestions;
|
||||||
|
|
||||||
for (auto& suggestion: suggestions)
|
for (auto& suggestion: suggestions)
|
||||||
quotedSuggestions.push_back("\"" + suggestion + "\"");
|
quotedSuggestions.emplace_back("\"" + suggestion + "\"");
|
||||||
|
|
||||||
return joinHumanReadable(quotedSuggestions, ", ", " or ");
|
return joinHumanReadable(quotedSuggestions, ", ", " or ");
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -72,4 +74,84 @@ std::string joinHumanReadable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats large numbers to be easily readable by humans.
|
||||||
|
/// Returns decimal representation for smaller numbers; hex for large numbers.
|
||||||
|
/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
|
||||||
|
/// formulaic form like 0x01 * 2**24 - 1.
|
||||||
|
/// @a T will typically by unsigned, u160, u256 or bigint.
|
||||||
|
/// @param _value to be formatted
|
||||||
|
/// @param _useTruncation if true, internal truncation is also applied,
|
||||||
|
/// like 0x5555...{+56 more}...5555
|
||||||
|
/// @example formatNumber((u256)0x7ffffff)
|
||||||
|
template <class T>
|
||||||
|
inline std::string formatNumberReadable(
|
||||||
|
T const& _value,
|
||||||
|
bool _useTruncation = false
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed,
|
||||||
|
"only unsigned types or bigint supported"
|
||||||
|
); //bigint does not carry sign bit on shift
|
||||||
|
|
||||||
|
// smaller numbers return as decimal
|
||||||
|
if (_value <= 0x1000000)
|
||||||
|
return _value.str();
|
||||||
|
|
||||||
|
HexCase hexcase = HexCase::Mixed;
|
||||||
|
HexPrefix prefix = HexPrefix::Add;
|
||||||
|
|
||||||
|
// when multiple trailing zero bytes, format as N * 2**x
|
||||||
|
int i = 0;
|
||||||
|
T v = _value;
|
||||||
|
for (; (v & 0xff) == 0; v >>= 8)
|
||||||
|
++i;
|
||||||
|
if (i > 2)
|
||||||
|
{
|
||||||
|
// 0x100 yields 2**8 (N is 1 and redundant)
|
||||||
|
if (v == 1)
|
||||||
|
return "2**" + std::to_string(i * 8);
|
||||||
|
return toHex(toCompactBigEndian(v), prefix, hexcase) +
|
||||||
|
" * 2**" +
|
||||||
|
std::to_string(i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when multiple trailing FF bytes, format as N * 2**x - 1
|
||||||
|
i = 0;
|
||||||
|
for (v = _value; (v & 0xff) == 0xff; v >>= 8)
|
||||||
|
++i;
|
||||||
|
if (i > 2)
|
||||||
|
{
|
||||||
|
// 0xFF yields 2**8 - 1 (v is 0 in that case)
|
||||||
|
if (v == 0)
|
||||||
|
return "2**" + std::to_string(i * 8) + " - 1";
|
||||||
|
return toHex(toCompactBigEndian(T(v + 1)), prefix, hexcase) +
|
||||||
|
" * 2**" + std::to_string(i * 8) +
|
||||||
|
" - 1";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str = toHex(toCompactBigEndian(_value), prefix, hexcase);
|
||||||
|
if (_useTruncation)
|
||||||
|
{
|
||||||
|
// return as interior-truncated hex.
|
||||||
|
int len = str.size();
|
||||||
|
|
||||||
|
if (len < 24)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
const int initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
|
||||||
|
const int finalChars = 4;
|
||||||
|
int numSkipped = len - initialChars - finalChars;
|
||||||
|
|
||||||
|
return str.substr(0, initialChars) +
|
||||||
|
"...{+" +
|
||||||
|
std::to_string(numSkipped) +
|
||||||
|
" more}..." +
|
||||||
|
str.substr(len-finalChars, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, show whole value.
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
|||||||
{
|
{
|
||||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||||
m_deposit += _i.deposit();
|
m_deposit += _i.deposit();
|
||||||
m_items.push_back(_i);
|
m_items.emplace_back(_i);
|
||||||
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
||||||
m_items.back().setLocation(m_currentSourceLocation);
|
m_items.back().setLocation(m_currentSourceLocation);
|
||||||
return back();
|
return back();
|
||||||
@ -353,14 +353,14 @@ AssemblyItem Assembly::namedTag(string const& _name)
|
|||||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||||
if (!m_namedTags.count(_name))
|
if (!m_namedTags.count(_name))
|
||||||
m_namedTags[_name] = size_t(newTag().data());
|
m_namedTags[_name] = size_t(newTag().data());
|
||||||
return AssemblyItem(Tag, m_namedTags.at(_name));
|
return AssemblyItem{Tag, m_namedTags.at(_name)};
|
||||||
}
|
}
|
||||||
|
|
||||||
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||||
{
|
{
|
||||||
h256 h(dev::keccak256(_identifier));
|
h256 h(dev::keccak256(_identifier));
|
||||||
m_libraries[h] = _identifier;
|
m_libraries[h] = _identifier;
|
||||||
return AssemblyItem(PushLibraryAddress, h);
|
return AssemblyItem{PushLibraryAddress, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
||||||
@ -415,14 +415,14 @@ map<u256, u256> Assembly::optimiseInternal(
|
|||||||
|
|
||||||
if (_settings.runJumpdestRemover)
|
if (_settings.runJumpdestRemover)
|
||||||
{
|
{
|
||||||
JumpdestRemover jumpdestOpt(m_items);
|
JumpdestRemover jumpdestOpt{m_items};
|
||||||
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
|
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_settings.runPeephole)
|
if (_settings.runPeephole)
|
||||||
{
|
{
|
||||||
PeepholeOptimiser peepOpt(m_items);
|
PeepholeOptimiser peepOpt{m_items};
|
||||||
while (peepOpt.optimise())
|
while (peepOpt.optimise())
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
@ -433,7 +433,7 @@ map<u256, u256> Assembly::optimiseInternal(
|
|||||||
// This only modifies PushTags, we have to run again to actually remove code.
|
// This only modifies PushTags, we have to run again to actually remove code.
|
||||||
if (_settings.runDeduplicate)
|
if (_settings.runDeduplicate)
|
||||||
{
|
{
|
||||||
BlockDeduplicator dedup(m_items);
|
BlockDeduplicator dedup{m_items};
|
||||||
if (dedup.deduplicate())
|
if (dedup.deduplicate())
|
||||||
{
|
{
|
||||||
tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end());
|
tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end());
|
||||||
@ -448,13 +448,13 @@ map<u256, u256> Assembly::optimiseInternal(
|
|||||||
// function types that can be stored in storage.
|
// function types that can be stored in storage.
|
||||||
AssemblyItems optimisedItems;
|
AssemblyItems optimisedItems;
|
||||||
|
|
||||||
bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end());
|
bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem{Instruction::MSIZE}) != m_items.end());
|
||||||
|
|
||||||
auto iter = m_items.begin();
|
auto iter = m_items.begin();
|
||||||
while (iter != m_items.end())
|
while (iter != m_items.end())
|
||||||
{
|
{
|
||||||
KnownState emptyState;
|
KnownState emptyState;
|
||||||
CommonSubexpressionEliminator eliminator(emptyState);
|
CommonSubexpressionEliminator eliminator{emptyState};
|
||||||
auto orig = iter;
|
auto orig = iter;
|
||||||
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
||||||
bool shouldReplace = false;
|
bool shouldReplace = false;
|
||||||
|
@ -45,8 +45,6 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
|
|||||||
class Assembly
|
class Assembly
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Assembly() {}
|
|
||||||
|
|
||||||
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
|
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
|
||||||
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
||||||
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
||||||
|
@ -110,7 +110,8 @@ int AssemblyItem::returnValues() const
|
|||||||
return 1;
|
return 1;
|
||||||
case Tag:
|
case Tag:
|
||||||
return 0;
|
return 0;
|
||||||
default:;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -135,9 +136,10 @@ bool AssemblyItem::canBeFunctional() const
|
|||||||
return true;
|
return true;
|
||||||
case Tag:
|
case Tag:
|
||||||
return false;
|
return false;
|
||||||
default:;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string AssemblyItem::getJumpTypeAsString() const
|
string AssemblyItem::getJumpTypeAsString() const
|
||||||
@ -168,7 +170,7 @@ string AssemblyItem::toAssemblyText() const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Push:
|
case Push:
|
||||||
text = toHex(toCompactBigEndian(data(), 1), 1, HexPrefix::Add);
|
text = toHex(toCompactBigEndian(data(), 1), HexPrefix::Add);
|
||||||
break;
|
break;
|
||||||
case PushString:
|
case PushString:
|
||||||
text = string("data_") + toHex(data());
|
text = string("data_") + toHex(data());
|
||||||
|
@ -57,22 +57,26 @@ class AssemblyItem
|
|||||||
public:
|
public:
|
||||||
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
||||||
|
|
||||||
AssemblyItem(u256 _push, langutil::SourceLocation const& _location = langutil::SourceLocation()):
|
AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
AssemblyItem(Push, _push, _location) { }
|
AssemblyItem(Push, std::move(_push), std::move(_location)) { }
|
||||||
AssemblyItem(solidity::Instruction _i, langutil::SourceLocation const& _location = langutil::SourceLocation()):
|
AssemblyItem(solidity::Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
m_type(Operation),
|
m_type(Operation),
|
||||||
m_instruction(_i),
|
m_instruction(_i),
|
||||||
m_location(_location)
|
m_location(std::move(_location))
|
||||||
{}
|
{}
|
||||||
AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation const& _location = langutil::SourceLocation()):
|
AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
m_type(_type),
|
m_type(_type),
|
||||||
m_location(_location)
|
m_location(std::move(_location))
|
||||||
{
|
{
|
||||||
if (m_type == Operation)
|
if (m_type == Operation)
|
||||||
m_instruction = Instruction(uint8_t(_data));
|
m_instruction = Instruction(uint8_t(_data));
|
||||||
else
|
else
|
||||||
m_data = std::make_shared<u256>(_data);
|
m_data = std::make_shared<u256>(std::move(_data));
|
||||||
}
|
}
|
||||||
|
AssemblyItem(AssemblyItem const&) = default;
|
||||||
|
AssemblyItem(AssemblyItem&&) = default;
|
||||||
|
AssemblyItem& operator=(AssemblyItem const&) = default;
|
||||||
|
AssemblyItem& operator=(AssemblyItem&&) = default;
|
||||||
|
|
||||||
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, data()); }
|
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, data()); }
|
||||||
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
|
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
|
||||||
@ -114,6 +118,13 @@ public:
|
|||||||
return data() < _other.data();
|
return data() < _other.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
|
||||||
|
bool operator==(Instruction _instr) const
|
||||||
|
{
|
||||||
|
return type() == Operation && instruction() == _instr;
|
||||||
|
}
|
||||||
|
bool operator!=(Instruction _instr) const { return !operator==(_instr); }
|
||||||
|
|
||||||
/// @returns an upper bound for the number of bytes required by this item, assuming that
|
/// @returns an upper bound for the number of bytes required by this item, assuming that
|
||||||
/// the value of a jump tag takes @a _addressLength bytes.
|
/// the value of a jump tag takes @a _addressLength bytes.
|
||||||
unsigned bytesRequired(unsigned _addressLength) const;
|
unsigned bytesRequired(unsigned _addressLength) const;
|
||||||
|
@ -41,7 +41,7 @@ bool BlockDeduplicator::deduplicate()
|
|||||||
|
|
||||||
// Virtual tag that signifies "the current block" and which is used to optimise loops.
|
// Virtual tag that signifies "the current block" and which is used to optimise loops.
|
||||||
// We abort if this virtual tag actually exists.
|
// We abort if this virtual tag actually exists.
|
||||||
AssemblyItem pushSelf(PushTag, u256(-4));
|
AssemblyItem pushSelf{PushTag, u256(-4)};
|
||||||
if (
|
if (
|
||||||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
|
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
|
||||||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
|
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
|
||||||
@ -55,17 +55,17 @@ bool BlockDeduplicator::deduplicate()
|
|||||||
|
|
||||||
// To compare recursive loops, we have to already unify PushTag opcodes of the
|
// To compare recursive loops, we have to already unify PushTag opcodes of the
|
||||||
// block's own tag.
|
// block's own tag.
|
||||||
AssemblyItem pushFirstTag(pushSelf);
|
AssemblyItem pushFirstTag{pushSelf};
|
||||||
AssemblyItem pushSecondTag(pushSelf);
|
AssemblyItem pushSecondTag{pushSelf};
|
||||||
|
|
||||||
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
|
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
|
||||||
pushFirstTag = m_items.at(_i).pushTag();
|
pushFirstTag = m_items.at(_i).pushTag();
|
||||||
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
|
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
|
||||||
pushSecondTag = m_items.at(_j).pushTag();
|
pushSecondTag = m_items.at(_j).pushTag();
|
||||||
|
|
||||||
BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
|
BlockIterator first{m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf};
|
||||||
BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
|
BlockIterator second{m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf};
|
||||||
BlockIterator end(m_items.end(), m_items.end());
|
BlockIterator end{m_items.end(), m_items.end()};
|
||||||
|
|
||||||
if (first != end && (*first).type() == Tag)
|
if (first != end && (*first).type() == Tag)
|
||||||
++first;
|
++first;
|
||||||
@ -126,7 +126,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
|
|||||||
{
|
{
|
||||||
if (it == end)
|
if (it == end)
|
||||||
return *this;
|
return *this;
|
||||||
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(Instruction::JUMPI))
|
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI})
|
||||||
it = end;
|
it = end;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,20 +1,38 @@
|
|||||||
set(sources
|
set(sources
|
||||||
Assembly.cpp
|
Assembly.cpp
|
||||||
|
Assembly.h
|
||||||
AssemblyItem.cpp
|
AssemblyItem.cpp
|
||||||
|
AssemblyItem.h
|
||||||
BlockDeduplicator.cpp
|
BlockDeduplicator.cpp
|
||||||
|
BlockDeduplicator.h
|
||||||
CommonSubexpressionEliminator.cpp
|
CommonSubexpressionEliminator.cpp
|
||||||
|
CommonSubexpressionEliminator.h
|
||||||
ConstantOptimiser.cpp
|
ConstantOptimiser.cpp
|
||||||
|
ConstantOptimiser.h
|
||||||
ControlFlowGraph.cpp
|
ControlFlowGraph.cpp
|
||||||
|
ControlFlowGraph.h
|
||||||
|
Exceptions.h
|
||||||
ExpressionClasses.cpp
|
ExpressionClasses.cpp
|
||||||
|
ExpressionClasses.h
|
||||||
GasMeter.cpp
|
GasMeter.cpp
|
||||||
|
GasMeter.h
|
||||||
Instruction.cpp
|
Instruction.cpp
|
||||||
|
Instruction.h
|
||||||
JumpdestRemover.cpp
|
JumpdestRemover.cpp
|
||||||
|
JumpdestRemover.h
|
||||||
KnownState.cpp
|
KnownState.cpp
|
||||||
|
KnownState.h
|
||||||
LinkerObject.cpp
|
LinkerObject.cpp
|
||||||
|
LinkerObject.h
|
||||||
PathGasMeter.cpp
|
PathGasMeter.cpp
|
||||||
|
PathGasMeter.h
|
||||||
PeepholeOptimiser.cpp
|
PeepholeOptimiser.cpp
|
||||||
|
PeepholeOptimiser.h
|
||||||
SemanticInformation.cpp
|
SemanticInformation.cpp
|
||||||
|
SemanticInformation.h
|
||||||
|
SimplificationRule.h
|
||||||
SimplificationRules.cpp
|
SimplificationRules.cpp
|
||||||
|
SimplificationRules.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(evmasm ${sources})
|
add_library(evmasm ${sources})
|
||||||
|
@ -87,7 +87,7 @@ void ControlFlowGraph::splitBlocks()
|
|||||||
m_blocks[id].begin = index;
|
m_blocks[id].begin = index;
|
||||||
}
|
}
|
||||||
if (item.type() == PushTag)
|
if (item.type() == PushTag)
|
||||||
m_blocks[id].pushedTags.push_back(BlockId(item.data()));
|
m_blocks[id].pushedTags.emplace_back(item.data());
|
||||||
if (SemanticInformation::altersControlFlow(item))
|
if (SemanticInformation::altersControlFlow(item))
|
||||||
{
|
{
|
||||||
m_blocks[id].end = index + 1;
|
m_blocks[id].end = index + 1;
|
||||||
|
@ -304,7 +304,7 @@ KnownState::StoreOperation KnownState::storeInStorage(
|
|||||||
|
|
||||||
AssemblyItem item(Instruction::SSTORE, _location);
|
AssemblyItem item(Instruction::SSTORE, _location);
|
||||||
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
|
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
|
||||||
StoreOperation operation(StoreOperation::Storage, _slot, m_sequenceNumber, id);
|
StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id};
|
||||||
m_storageContent[_slot] = _value;
|
m_storageContent[_slot] = _value;
|
||||||
// increment a second time so that we get unique sequence numbers for writes
|
// increment a second time so that we get unique sequence numbers for writes
|
||||||
m_sequenceNumber++;
|
m_sequenceNumber++;
|
||||||
@ -336,7 +336,7 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source
|
|||||||
|
|
||||||
AssemblyItem item(Instruction::MSTORE, _location);
|
AssemblyItem item(Instruction::MSTORE, _location);
|
||||||
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
|
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
|
||||||
StoreOperation operation(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
|
StoreOperation operation{StoreOperation::Memory, _slot, m_sequenceNumber, id};
|
||||||
m_memoryContent[_slot] = _value;
|
m_memoryContent[_slot] = _value;
|
||||||
// increment a second time so that we get unique sequence numbers for writes
|
// increment a second time so that we get unique sequence numbers for writes
|
||||||
m_sequenceNumber++;
|
m_sequenceNumber++;
|
||||||
|
@ -74,18 +74,13 @@ public:
|
|||||||
struct StoreOperation
|
struct StoreOperation
|
||||||
{
|
{
|
||||||
enum Target { Invalid, Memory, Storage };
|
enum Target { Invalid, Memory, Storage };
|
||||||
StoreOperation(): target(Invalid), sequenceNumber(-1) {}
|
|
||||||
StoreOperation(
|
|
||||||
Target _target,
|
|
||||||
Id _slot,
|
|
||||||
unsigned _sequenceNumber,
|
|
||||||
Id _expression
|
|
||||||
): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
|
|
||||||
bool isValid() const { return target != Invalid; }
|
bool isValid() const { return target != Invalid; }
|
||||||
Target target;
|
|
||||||
Id slot;
|
Target target{Invalid};
|
||||||
unsigned sequenceNumber;
|
Id slot{std::numeric_limits<Id>::max()};
|
||||||
Id expression;
|
unsigned sequenceNumber{std::numeric_limits<unsigned>::max()};
|
||||||
|
Id expression{std::numeric_limits<Id>::max()};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit KnownState(
|
explicit KnownState(
|
||||||
|
@ -160,8 +160,7 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2>
|
|||||||
{
|
{
|
||||||
// Remove SWAP1 if following instruction is commutative
|
// Remove SWAP1 if following instruction is commutative
|
||||||
if (
|
if (
|
||||||
_swap.type() == Operation &&
|
_swap == Instruction::SWAP1 &&
|
||||||
_swap.instruction() == Instruction::SWAP1 &&
|
|
||||||
SemanticInformation::isCommutativeOperation(_op)
|
SemanticInformation::isCommutativeOperation(_op)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -177,7 +176,7 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
|
|||||||
{
|
{
|
||||||
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
|
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
|
||||||
{
|
{
|
||||||
map<Instruction, Instruction> swappableOps{
|
static map<Instruction, Instruction> const swappableOps{
|
||||||
{ Instruction::LT, Instruction::GT },
|
{ Instruction::LT, Instruction::GT },
|
||||||
{ Instruction::GT, Instruction::LT },
|
{ Instruction::GT, Instruction::LT },
|
||||||
{ Instruction::SLT, Instruction::SGT },
|
{ Instruction::SLT, Instruction::SGT },
|
||||||
@ -185,8 +184,7 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
_swap.type() == Operation &&
|
_swap == Instruction::SWAP1 &&
|
||||||
_swap.instruction() == Instruction::SWAP1 &&
|
|
||||||
_op.type() == Operation &&
|
_op.type() == Operation &&
|
||||||
swappableOps.count(_op.instruction())
|
swappableOps.count(_op.instruction())
|
||||||
)
|
)
|
||||||
|
@ -108,7 +108,7 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
|
|||||||
|
|
||||||
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
|
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
|
||||||
{
|
{
|
||||||
return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI);
|
return _item == Instruction::JUMP || _item == Instruction::JUMPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
|
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
|
||||||
|
@ -209,7 +209,7 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation c
|
|||||||
item = _pattern.toAssemblyItem(_location);
|
item = _pattern.toAssemblyItem(_location);
|
||||||
}
|
}
|
||||||
for (auto const& arg: _pattern.arguments())
|
for (auto const& arg: _pattern.arguments())
|
||||||
arguments.push_back(ExpressionTemplate(arg, _location));
|
arguments.emplace_back(arg, _location);
|
||||||
}
|
}
|
||||||
|
|
||||||
string ExpressionTemplate::toString() const
|
string ExpressionTemplate::toString() const
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
|
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
|
||||||
set(sources
|
set(sources
|
||||||
CharStream.cpp
|
CharStream.cpp
|
||||||
|
CharStream.h
|
||||||
ErrorReporter.cpp
|
ErrorReporter.cpp
|
||||||
|
ErrorReporter.h
|
||||||
|
EVMVersion.h
|
||||||
Exceptions.cpp
|
Exceptions.cpp
|
||||||
|
Exceptions.h
|
||||||
ParserBase.cpp
|
ParserBase.cpp
|
||||||
|
ParserBase.h
|
||||||
Scanner.cpp
|
Scanner.cpp
|
||||||
|
Scanner.h
|
||||||
|
SourceLocation.h
|
||||||
|
SourceReferenceExtractor.cpp
|
||||||
|
SourceReferenceExtractor.h
|
||||||
SourceReferenceFormatter.cpp
|
SourceReferenceFormatter.cpp
|
||||||
|
SourceReferenceFormatter.h
|
||||||
Token.cpp
|
Token.cpp
|
||||||
|
Token.h
|
||||||
|
UndefMacros.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(langutil ${sources})
|
add_library(langutil ${sources})
|
||||||
|
@ -67,9 +67,9 @@ namespace langutil
|
|||||||
class CharStream
|
class CharStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CharStream(): m_position(0) {}
|
CharStream() = default;
|
||||||
explicit CharStream(std::string const& _source, std::string const& name):
|
explicit CharStream(std::string const& _source, std::string const& name):
|
||||||
m_source(_source), m_name(name), m_position(0) {}
|
m_source(_source), m_name(name) {}
|
||||||
|
|
||||||
int position() const { return m_position; }
|
int position() const { return m_position; }
|
||||||
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
|
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
|
||||||
@ -94,7 +94,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::string m_source;
|
std::string m_source;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
size_t m_position;
|
size_t m_position{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class EVMVersion:
|
|||||||
boost::equality_comparable<EVMVersion>
|
boost::equality_comparable<EVMVersion>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EVMVersion() {}
|
EVMVersion() = default;
|
||||||
|
|
||||||
static EVMVersion homestead() { return {Version::Homestead}; }
|
static EVMVersion homestead() { return {Version::Homestead}; }
|
||||||
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
|
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
|
||||||
|
@ -105,7 +105,7 @@ class SecondarySourceLocation
|
|||||||
public:
|
public:
|
||||||
SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
|
SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
|
||||||
{
|
{
|
||||||
infos.push_back(std::make_pair(_errMsg, _sourceLocation));
|
infos.emplace_back(_errMsg, _sourceLocation);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,10 +100,10 @@ void ParserBase::decreaseRecursionDepth()
|
|||||||
|
|
||||||
void ParserBase::parserError(string const& _description)
|
void ParserBase::parserError(string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.parserError(SourceLocation(position(), endPosition(), source()), _description);
|
m_errorReporter.parserError(SourceLocation{position(), endPosition(), source()}, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::fatalParserError(string const& _description)
|
void ParserBase::fatalParserError(string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), source()), _description);
|
m_errorReporter.fatalParserError(SourceLocation{position(), endPosition(), source()}, _description);
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,6 @@ namespace langutil
|
|||||||
*/
|
*/
|
||||||
struct SourceLocation
|
struct SourceLocation
|
||||||
{
|
{
|
||||||
SourceLocation(): start(-1), end(-1), source{nullptr} { }
|
|
||||||
SourceLocation(int _start, int _end, std::shared_ptr<CharStream> _source):
|
|
||||||
start(_start), end(_end), source{std::move(_source)} { }
|
|
||||||
|
|
||||||
bool operator==(SourceLocation const& _other) const
|
bool operator==(SourceLocation const& _other) const
|
||||||
{
|
{
|
||||||
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
|
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
|
||||||
@ -53,8 +49,8 @@ struct SourceLocation
|
|||||||
|
|
||||||
bool isEmpty() const { return start == -1 && end == -1; }
|
bool isEmpty() const { return start == -1 && end == -1; }
|
||||||
|
|
||||||
int start;
|
int start = -1;
|
||||||
int end;
|
int end = -1;
|
||||||
std::shared_ptr<CharStream> source;
|
std::shared_ptr<CharStream> source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
89
liblangutil/SourceReferenceExtractor.cpp
Normal file
89
liblangutil/SourceReferenceExtractor.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
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 <liblangutil/SourceReferenceExtractor.h>
|
||||||
|
#include <liblangutil/CharStream.h>
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace langutil;
|
||||||
|
|
||||||
|
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
|
||||||
|
{
|
||||||
|
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||||
|
|
||||||
|
string const* message = boost::get_error_info<errinfo_comment>(_exception);
|
||||||
|
SourceReference primary = extract(location, message ? *message : "");
|
||||||
|
|
||||||
|
std::vector<SourceReference> secondary;
|
||||||
|
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
||||||
|
if (secondaryLocation && !secondaryLocation->infos.empty())
|
||||||
|
for (auto const& info: secondaryLocation->infos)
|
||||||
|
secondary.emplace_back(extract(&info.second, info.first));
|
||||||
|
|
||||||
|
return Message{std::move(primary), _category, std::move(secondary)};
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message)
|
||||||
|
{
|
||||||
|
if (!_location || !_location->source.get()) // Nothing we can extract here
|
||||||
|
return SourceReference::MessageOnly(std::move(message));
|
||||||
|
|
||||||
|
shared_ptr<CharStream> const& source = _location->source;
|
||||||
|
|
||||||
|
LineColumn const interest = source->translatePositionToLineColumn(_location->start);
|
||||||
|
LineColumn start = interest;
|
||||||
|
LineColumn end = source->translatePositionToLineColumn(_location->end);
|
||||||
|
bool const isMultiline = start.line != end.line;
|
||||||
|
|
||||||
|
string line = source->lineAtPosition(_location->start);
|
||||||
|
|
||||||
|
int locationLength = isMultiline ? line.length() - start.column : end.column - start.column;
|
||||||
|
if (locationLength > 150)
|
||||||
|
{
|
||||||
|
line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35);
|
||||||
|
end.column = start.column + 75;
|
||||||
|
locationLength = 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.length() > 150)
|
||||||
|
{
|
||||||
|
int const len = line.length();
|
||||||
|
line = line.substr(max(0, start.column - 35), min(start.column, 35) + min(locationLength + 35, len - start.column));
|
||||||
|
if (start.column + locationLength + 35 < len)
|
||||||
|
line += " ...";
|
||||||
|
if (start.column > 35)
|
||||||
|
{
|
||||||
|
line = " ... " + line;
|
||||||
|
start.column = 40;
|
||||||
|
}
|
||||||
|
end.column = start.column + locationLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SourceReference{
|
||||||
|
std::move(message),
|
||||||
|
source->name(),
|
||||||
|
interest,
|
||||||
|
isMultiline,
|
||||||
|
line,
|
||||||
|
start.column,
|
||||||
|
end.column,
|
||||||
|
};
|
||||||
|
}
|
75
liblangutil/SourceReferenceExtractor.h
Normal file
75
liblangutil/SourceReferenceExtractor.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 <iosfwd>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
struct Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace langutil
|
||||||
|
{
|
||||||
|
|
||||||
|
struct LineColumn
|
||||||
|
{
|
||||||
|
int line = {-1};
|
||||||
|
int column = {-1};
|
||||||
|
|
||||||
|
LineColumn() = default;
|
||||||
|
LineColumn(std::tuple<int, int> const& _t): line{std::get<0>(_t)}, column{std::get<1>(_t)} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourceReference
|
||||||
|
{
|
||||||
|
std::string message; ///< A message that relates to this source reference (such as a warning or an error message).
|
||||||
|
std::string sourceName; ///< Underlying source name (for example the filename).
|
||||||
|
LineColumn position; ///< Actual (error) position this source reference is surrounding.
|
||||||
|
bool multiline = {false}; ///< Indicates whether the actual SourceReference is truncated to one line.
|
||||||
|
std::string text; ///< Extracted source code text (potentially truncated if multiline or too long).
|
||||||
|
int startColumn = {-1}; ///< Highlighting range-start of text field.
|
||||||
|
int endColumn = {-1}; ///< Highlighting range-end of text field.
|
||||||
|
|
||||||
|
/// Constructs a SourceReference containing a message only.
|
||||||
|
static SourceReference MessageOnly(std::string _msg)
|
||||||
|
{
|
||||||
|
SourceReference sref;
|
||||||
|
sref.message = std::move(_msg);
|
||||||
|
return sref;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourceLocation;
|
||||||
|
|
||||||
|
namespace SourceReferenceExtractor
|
||||||
|
{
|
||||||
|
struct Message
|
||||||
|
{
|
||||||
|
SourceReference primary;
|
||||||
|
std::string category; // "Error", "Warning", ...
|
||||||
|
std::vector<SourceReference> secondary;
|
||||||
|
};
|
||||||
|
|
||||||
|
Message extract(dev::Exception const& _exception, std::string _category);
|
||||||
|
SourceReference extract(SourceLocation const* _location, std::string message = "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,100 +30,63 @@ using namespace langutil;
|
|||||||
|
|
||||||
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
||||||
{
|
{
|
||||||
if (!_location || !_location->source)
|
printSourceLocation(SourceReferenceExtractor::extract(_location));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref)
|
||||||
|
{
|
||||||
|
if (_ref.position.line < 0)
|
||||||
return; // Nothing we can print here
|
return; // Nothing we can print here
|
||||||
auto const& scanner = m_scannerFromSourceName(_location->source->name());
|
|
||||||
int startLine;
|
|
||||||
int startColumn;
|
|
||||||
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
|
||||||
int endLine;
|
|
||||||
int endColumn;
|
|
||||||
tie(endLine, endColumn) = scanner.translatePositionToLineColumn(_location->end);
|
|
||||||
if (startLine == endLine)
|
|
||||||
{
|
|
||||||
string line = scanner.lineAtPosition(_location->start);
|
|
||||||
|
|
||||||
int locationLength = endColumn - startColumn;
|
if (!_ref.multiline)
|
||||||
if (locationLength > 150)
|
|
||||||
{
|
{
|
||||||
line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35);
|
m_stream << _ref.text << endl;
|
||||||
endColumn = startColumn + 75;
|
|
||||||
locationLength = 75;
|
|
||||||
}
|
|
||||||
if (line.length() > 150)
|
|
||||||
{
|
|
||||||
int len = line.length();
|
|
||||||
line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
|
|
||||||
if (startColumn + locationLength + 35 < len)
|
|
||||||
line += " ...";
|
|
||||||
if (startColumn > 35)
|
|
||||||
{
|
|
||||||
line = " ... " + line;
|
|
||||||
startColumn = 40;
|
|
||||||
}
|
|
||||||
endColumn = startColumn + locationLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stream << line << endl;
|
|
||||||
|
|
||||||
|
// mark the text-range like this: ^-----^
|
||||||
for_each(
|
for_each(
|
||||||
line.cbegin(),
|
_ref.text.cbegin(),
|
||||||
line.cbegin() + startColumn,
|
_ref.text.cbegin() + _ref.startColumn,
|
||||||
[this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
[this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
||||||
);
|
);
|
||||||
m_stream << "^";
|
m_stream << "^";
|
||||||
if (endColumn > startColumn + 2)
|
if (_ref.endColumn > _ref.startColumn + 2)
|
||||||
m_stream << string(endColumn - startColumn - 2, '-');
|
m_stream << string(_ref.endColumn - _ref.startColumn - 2, '-');
|
||||||
if (endColumn > startColumn + 1)
|
if (_ref.endColumn > _ref.startColumn + 1)
|
||||||
m_stream << "^";
|
m_stream << "^";
|
||||||
m_stream << endl;
|
m_stream << endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_stream <<
|
m_stream <<
|
||||||
scanner.lineAtPosition(_location->start) <<
|
_ref.text <<
|
||||||
endl <<
|
endl <<
|
||||||
string(startColumn, ' ') <<
|
string(_ref.startColumn, ' ') <<
|
||||||
"^ (Relevant source part starts here and spans across multiple lines)." <<
|
"^ (Relevant source part starts here and spans across multiple lines)." <<
|
||||||
endl;
|
endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
|
void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
|
||||||
{
|
{
|
||||||
if (!_location || !_location->source)
|
if (_ref.position.line != -1)
|
||||||
return; // Nothing we can print here
|
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
|
||||||
auto const& scanner = m_scannerFromSourceName(_location->source->name());
|
|
||||||
int startLine;
|
|
||||||
int startColumn;
|
|
||||||
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
|
|
||||||
m_stream << _location->source->name() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceReferenceFormatter::printExceptionInformation(
|
void SourceReferenceFormatter::printExceptionInformation(dev::Exception const& _error, std::string const& _category)
|
||||||
dev::Exception const& _exception,
|
|
||||||
string const& _name
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
printExceptionInformation(SourceReferenceExtractor::extract(_error, _category));
|
||||||
auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
|
||||||
|
|
||||||
printSourceName(location);
|
|
||||||
|
|
||||||
m_stream << _name;
|
|
||||||
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
|
||||||
m_stream << ": " << *description << endl;
|
|
||||||
else
|
|
||||||
m_stream << endl;
|
|
||||||
|
|
||||||
printSourceLocation(location);
|
|
||||||
|
|
||||||
if (secondarylocation && !secondarylocation->infos.empty())
|
|
||||||
{
|
|
||||||
for (auto info: secondarylocation->infos)
|
|
||||||
{
|
|
||||||
printSourceName(&info.second);
|
|
||||||
m_stream << info.first << endl;
|
|
||||||
printSourceLocation(&info.second);
|
|
||||||
}
|
}
|
||||||
m_stream << endl;
|
|
||||||
|
void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg)
|
||||||
|
{
|
||||||
|
printSourceName(_msg.primary);
|
||||||
|
|
||||||
|
m_stream << _msg.category << ": " << _msg.primary.message << endl;
|
||||||
|
|
||||||
|
printSourceLocation(_msg.primary);
|
||||||
|
|
||||||
|
for (auto const& ref: _msg.secondary)
|
||||||
|
{
|
||||||
|
printSourceName(ref);
|
||||||
|
m_stream << ref.message << endl;
|
||||||
|
printSourceLocation(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <liblangutil/SourceReferenceExtractor.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -39,38 +40,33 @@ class Scanner;
|
|||||||
class SourceReferenceFormatter
|
class SourceReferenceFormatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ScannerFromSourceNameFun = std::function<langutil::Scanner const&(std::string const&)>;
|
explicit SourceReferenceFormatter(std::ostream& _stream):
|
||||||
|
m_stream(_stream)
|
||||||
explicit SourceReferenceFormatter(
|
|
||||||
std::ostream& _stream,
|
|
||||||
ScannerFromSourceNameFun _scannerFromSourceName
|
|
||||||
):
|
|
||||||
m_stream(_stream),
|
|
||||||
m_scannerFromSourceName(std::move(_scannerFromSourceName))
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// Prints source location if it is given.
|
/// Prints source location if it is given.
|
||||||
void printSourceLocation(langutil::SourceLocation const* _location);
|
void printSourceLocation(SourceLocation const* _location);
|
||||||
void printExceptionInformation(dev::Exception const& _exception, std::string const& _name);
|
void printSourceLocation(SourceReference const& _ref);
|
||||||
|
void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
|
||||||
|
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
|
||||||
|
|
||||||
static std::string formatExceptionInformation(
|
static std::string formatExceptionInformation(
|
||||||
dev::Exception const& _exception,
|
dev::Exception const& _exception,
|
||||||
std::string const& _name,
|
std::string const& _name
|
||||||
ScannerFromSourceNameFun const& _scannerFromSourceName
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::ostringstream errorOutput;
|
std::ostringstream errorOutput;
|
||||||
|
|
||||||
SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName);
|
SourceReferenceFormatter formatter(errorOutput);
|
||||||
formatter.printExceptionInformation(_exception, _name);
|
formatter.printExceptionInformation(_exception, _name);
|
||||||
return errorOutput.str();
|
return errorOutput.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Prints source name if location is given.
|
/// Prints source name if location is given.
|
||||||
void printSourceName(langutil::SourceLocation const* _location);
|
void printSourceName(SourceReference const& _ref);
|
||||||
|
|
||||||
std::ostream& m_stream;
|
std::ostream& m_stream;
|
||||||
ScannerFromSourceNameFun m_scannerFromSourceName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
set(sources
|
set(sources
|
||||||
CodeFragment.cpp
|
CodeFragment.cpp
|
||||||
|
CodeFragment.h
|
||||||
Compiler.cpp
|
Compiler.cpp
|
||||||
|
Compiler.h
|
||||||
CompilerState.cpp
|
CompilerState.cpp
|
||||||
|
CompilerState.h
|
||||||
|
Exceptions.h
|
||||||
Parser.cpp
|
Parser.cpp
|
||||||
|
Parser.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(lll ${sources})
|
add_library(lll ${sources})
|
||||||
|
@ -353,7 +353,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
if (j.tag() || j.which() != sp::utree_type::symbol_type)
|
if (j.tag() || j.which() != sp::utree_type::symbol_type)
|
||||||
error<InvalidMacroArgs>();
|
error<InvalidMacroArgs>();
|
||||||
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
||||||
args.push_back(string(sr.begin(), sr.end()));
|
args.emplace_back(sr.begin(), sr.end());
|
||||||
}
|
}
|
||||||
else if (ii == 3)
|
else if (ii == 3)
|
||||||
{
|
{
|
||||||
@ -464,9 +464,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
if (c++)
|
if (c++)
|
||||||
{
|
{
|
||||||
if (us == "LLL" && c == 1)
|
if (us == "LLL" && c == 1)
|
||||||
code.push_back(CodeFragment(i, ns, m_readFile));
|
code.emplace_back(i, ns, m_readFile);
|
||||||
else
|
else
|
||||||
code.push_back(CodeFragment(i, _s, m_readFile));
|
code.emplace_back(i, _s, m_readFile);
|
||||||
}
|
}
|
||||||
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
|
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
|
||||||
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
|
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
|
||||||
|
@ -41,7 +41,7 @@ class CodeFragment
|
|||||||
public:
|
public:
|
||||||
using ReadCallback = std::function<std::string(std::string const&)>;
|
using ReadCallback = std::function<std::string(std::string const&)>;
|
||||||
|
|
||||||
CodeFragment() {}
|
CodeFragment() = default;
|
||||||
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
|
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
|
||||||
|
|
||||||
static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile);
|
static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile);
|
||||||
|
@ -46,22 +46,22 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer
|
|||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
{
|
{
|
||||||
_errors->push_back("Parse error.");
|
_errors->emplace_back("Parse error.");
|
||||||
_errors->push_back(boost::diagnostic_information(_e));
|
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception const& _e)
|
catch (std::exception const& _e)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
{
|
{
|
||||||
_errors->push_back("Parse exception.");
|
_errors->emplace_back("Parse exception.");
|
||||||
_errors->push_back(boost::diagnostic_information(_e));
|
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
_errors->push_back("Internal compiler exception.");
|
_errors->emplace_back("Internal compiler exception.");
|
||||||
}
|
}
|
||||||
return bytes();
|
return bytes();
|
||||||
}
|
}
|
||||||
@ -84,22 +84,22 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe
|
|||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
{
|
{
|
||||||
_errors->push_back("Parse error.");
|
_errors->emplace_back("Parse error.");
|
||||||
_errors->push_back(boost::diagnostic_information(_e));
|
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception const& _e)
|
catch (std::exception const& _e)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
{
|
{
|
||||||
_errors->push_back("Parse exception.");
|
_errors->emplace_back("Parse exception.");
|
||||||
_errors->push_back(boost::diagnostic_information(_e));
|
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
_errors->push_back("Internal compiler exception.");
|
_errors->emplace_back("Internal compiler exception.");
|
||||||
}
|
}
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
if (EMSCRIPTEN)
|
if (EMSCRIPTEN)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20")
|
||||||
add_executable(soljson libsolc.cpp)
|
add_executable(soljson libsolc.cpp libsolc.h)
|
||||||
target_link_libraries(soljson PRIVATE solidity)
|
target_link_libraries(soljson PRIVATE solidity)
|
||||||
else()
|
else()
|
||||||
add_library(libsolc libsolc.cpp)
|
add_library(libsolc libsolc.cpp libsolc.h)
|
||||||
set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc)
|
set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc)
|
||||||
target_link_libraries(libsolc PRIVATE solidity)
|
target_link_libraries(libsolc PRIVATE solidity)
|
||||||
endif()
|
endif()
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
* Public compiler API.
|
* Public compiler API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -1,68 +1,121 @@
|
|||||||
# Until we have a clear separation, libyul has to be included here
|
# Until we have a clear separation, libyul has to be included here
|
||||||
set(sources
|
set(sources
|
||||||
analysis/ConstantEvaluator.cpp
|
analysis/ConstantEvaluator.cpp
|
||||||
|
analysis/ConstantEvaluator.h
|
||||||
analysis/ContractLevelChecker.cpp
|
analysis/ContractLevelChecker.cpp
|
||||||
|
analysis/ContractLevelChecker.h
|
||||||
analysis/ControlFlowAnalyzer.cpp
|
analysis/ControlFlowAnalyzer.cpp
|
||||||
|
analysis/ControlFlowAnalyzer.h
|
||||||
analysis/ControlFlowBuilder.cpp
|
analysis/ControlFlowBuilder.cpp
|
||||||
|
analysis/ControlFlowBuilder.h
|
||||||
analysis/ControlFlowGraph.cpp
|
analysis/ControlFlowGraph.cpp
|
||||||
|
analysis/ControlFlowGraph.h
|
||||||
analysis/DeclarationContainer.cpp
|
analysis/DeclarationContainer.cpp
|
||||||
|
analysis/DeclarationContainer.h
|
||||||
analysis/DocStringAnalyser.cpp
|
analysis/DocStringAnalyser.cpp
|
||||||
|
analysis/DocStringAnalyser.h
|
||||||
analysis/GlobalContext.cpp
|
analysis/GlobalContext.cpp
|
||||||
|
analysis/GlobalContext.h
|
||||||
analysis/NameAndTypeResolver.cpp
|
analysis/NameAndTypeResolver.cpp
|
||||||
|
analysis/NameAndTypeResolver.h
|
||||||
analysis/PostTypeChecker.cpp
|
analysis/PostTypeChecker.cpp
|
||||||
|
analysis/PostTypeChecker.h
|
||||||
analysis/ReferencesResolver.cpp
|
analysis/ReferencesResolver.cpp
|
||||||
|
analysis/ReferencesResolver.h
|
||||||
analysis/SemVerHandler.cpp
|
analysis/SemVerHandler.cpp
|
||||||
|
analysis/SemVerHandler.h
|
||||||
analysis/StaticAnalyzer.cpp
|
analysis/StaticAnalyzer.cpp
|
||||||
|
analysis/StaticAnalyzer.h
|
||||||
analysis/SyntaxChecker.cpp
|
analysis/SyntaxChecker.cpp
|
||||||
|
analysis/SyntaxChecker.h
|
||||||
analysis/TypeChecker.cpp
|
analysis/TypeChecker.cpp
|
||||||
|
analysis/TypeChecker.h
|
||||||
analysis/ViewPureChecker.cpp
|
analysis/ViewPureChecker.cpp
|
||||||
|
analysis/ViewPureChecker.h
|
||||||
ast/AST.cpp
|
ast/AST.cpp
|
||||||
|
ast/AST.h
|
||||||
|
ast/AST_accept.h
|
||||||
ast/ASTAnnotations.cpp
|
ast/ASTAnnotations.cpp
|
||||||
|
ast/ASTAnnotations.h
|
||||||
|
ast/ASTEnums.h
|
||||||
|
ast/ASTForward.h
|
||||||
ast/ASTJsonConverter.cpp
|
ast/ASTJsonConverter.cpp
|
||||||
|
ast/ASTJsonConverter.h
|
||||||
ast/ASTPrinter.cpp
|
ast/ASTPrinter.cpp
|
||||||
|
ast/ASTPrinter.h
|
||||||
|
ast/ASTVisitor.h
|
||||||
|
ast/ExperimentalFeatures.h
|
||||||
ast/Types.cpp
|
ast/Types.cpp
|
||||||
|
ast/Types.h
|
||||||
codegen/ABIFunctions.cpp
|
codegen/ABIFunctions.cpp
|
||||||
|
codegen/ABIFunctions.h
|
||||||
codegen/ArrayUtils.cpp
|
codegen/ArrayUtils.cpp
|
||||||
|
codegen/ArrayUtils.h
|
||||||
|
codegen/AsmCodeGen.cpp
|
||||||
|
codegen/AsmCodeGen.h
|
||||||
codegen/Compiler.cpp
|
codegen/Compiler.cpp
|
||||||
|
codegen/Compiler.h
|
||||||
codegen/CompilerContext.cpp
|
codegen/CompilerContext.cpp
|
||||||
|
codegen/CompilerContext.h
|
||||||
codegen/CompilerUtils.cpp
|
codegen/CompilerUtils.cpp
|
||||||
|
codegen/CompilerUtils.h
|
||||||
codegen/ContractCompiler.cpp
|
codegen/ContractCompiler.cpp
|
||||||
|
codegen/ContractCompiler.h
|
||||||
codegen/ExpressionCompiler.cpp
|
codegen/ExpressionCompiler.cpp
|
||||||
|
codegen/ExpressionCompiler.h
|
||||||
codegen/LValue.cpp
|
codegen/LValue.cpp
|
||||||
|
codegen/LValue.h
|
||||||
formal/SMTChecker.cpp
|
formal/SMTChecker.cpp
|
||||||
|
formal/SMTChecker.h
|
||||||
formal/SMTLib2Interface.cpp
|
formal/SMTLib2Interface.cpp
|
||||||
|
formal/SMTLib2Interface.h
|
||||||
formal/SMTPortfolio.cpp
|
formal/SMTPortfolio.cpp
|
||||||
|
formal/SMTPortfolio.h
|
||||||
|
formal/SolverInterface.h
|
||||||
formal/SSAVariable.cpp
|
formal/SSAVariable.cpp
|
||||||
|
formal/SSAVariable.h
|
||||||
formal/SymbolicTypes.cpp
|
formal/SymbolicTypes.cpp
|
||||||
|
formal/SymbolicTypes.h
|
||||||
formal/SymbolicVariables.cpp
|
formal/SymbolicVariables.cpp
|
||||||
|
formal/SymbolicVariables.h
|
||||||
formal/VariableUsage.cpp
|
formal/VariableUsage.cpp
|
||||||
|
formal/VariableUsage.h
|
||||||
interface/ABI.cpp
|
interface/ABI.cpp
|
||||||
|
interface/ABI.h
|
||||||
interface/AssemblyStack.cpp
|
interface/AssemblyStack.cpp
|
||||||
|
interface/AssemblyStack.h
|
||||||
interface/CompilerStack.cpp
|
interface/CompilerStack.cpp
|
||||||
|
interface/CompilerStack.h
|
||||||
interface/GasEstimator.cpp
|
interface/GasEstimator.cpp
|
||||||
|
interface/GasEstimator.h
|
||||||
interface/Natspec.cpp
|
interface/Natspec.cpp
|
||||||
|
interface/Natspec.h
|
||||||
|
interface/ReadFile.h
|
||||||
interface/StandardCompiler.cpp
|
interface/StandardCompiler.cpp
|
||||||
|
interface/StandardCompiler.h
|
||||||
interface/Version.cpp
|
interface/Version.cpp
|
||||||
|
interface/Version.h
|
||||||
parsing/DocStringParser.cpp
|
parsing/DocStringParser.cpp
|
||||||
|
parsing/DocStringParser.h
|
||||||
parsing/Parser.cpp
|
parsing/Parser.cpp
|
||||||
|
parsing/Parser.h
|
||||||
|
parsing/Token.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Z3 QUIET)
|
find_package(Z3 QUIET)
|
||||||
if (${Z3_FOUND})
|
if (${Z3_FOUND})
|
||||||
include_directories(${Z3_INCLUDE_DIR})
|
|
||||||
add_definitions(-DHAVE_Z3)
|
add_definitions(-DHAVE_Z3)
|
||||||
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
|
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
|
||||||
set(z3_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
|
set(z3_SRCS formal/Z3Interface.cpp formal/Z3Interface.h)
|
||||||
else()
|
else()
|
||||||
set(z3_SRCS)
|
set(z3_SRCS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(CVC4 QUIET)
|
find_package(CVC4 QUIET)
|
||||||
if (${CVC4_FOUND})
|
if (${CVC4_FOUND})
|
||||||
include_directories(${CVC4_INCLUDE_DIR})
|
|
||||||
add_definitions(-DHAVE_CVC4)
|
add_definitions(-DHAVE_CVC4)
|
||||||
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
|
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
|
||||||
set(cvc4_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
|
set(cvc4_SRCS formal/CVC4Interface.cpp formal/CVC4Interface.h)
|
||||||
else()
|
else()
|
||||||
set(cvc4_SRCS)
|
set(cvc4_SRCS)
|
||||||
endif()
|
endif()
|
||||||
@ -76,9 +129,9 @@ add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
|||||||
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
|
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
|
||||||
|
|
||||||
if (${Z3_FOUND})
|
if (${Z3_FOUND})
|
||||||
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
|
target_link_libraries(solidity PUBLIC Z3::Z3)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (${CVC4_FOUND})
|
if (${CVC4_FOUND})
|
||||||
target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES})
|
target_link_libraries(solidity PUBLIC CVC4::CVC4)
|
||||||
endif()
|
endif()
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
|||||||
auto right = type(_operation.rightExpression());
|
auto right = type(_operation.rightExpression());
|
||||||
if (left && right)
|
if (left && right)
|
||||||
{
|
{
|
||||||
auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||||
if (!commonType)
|
if (!commonType)
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
_operation.location(),
|
_operation.location(),
|
||||||
|
@ -20,10 +20,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ContractLevelChecker.h>
|
#include <libsolidity/analysis/ContractLevelChecker.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
|
||||||
@ -227,7 +226,7 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con
|
|||||||
return _type->hasEqualParameterTypes(*_funAndFlag.first);
|
return _type->hasEqualParameterTypes(*_funAndFlag.first);
|
||||||
});
|
});
|
||||||
if (it == overloads.end())
|
if (it == overloads.end())
|
||||||
overloads.push_back(make_pair(_type, _implemented));
|
overloads.emplace_back(_type, _implemented);
|
||||||
else if (it->second)
|
else if (it->second)
|
||||||
{
|
{
|
||||||
if (!_implemented)
|
if (!_implemented)
|
||||||
@ -409,8 +408,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
|||||||
auto functionType = make_shared<FunctionType>(*f);
|
auto functionType = make_shared<FunctionType>(*f);
|
||||||
// under non error circumstances this should be true
|
// under non error circumstances this should be true
|
||||||
if (functionType->interfaceFunctionType())
|
if (functionType->interfaceFunctionType())
|
||||||
externalDeclarations[functionType->externalSignature()].push_back(
|
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||||
make_pair(f, functionType->asCallableFunction(false))
|
f, functionType->asCallableFunction(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (VariableDeclaration const* v: contract->stateVariables())
|
for (VariableDeclaration const* v: contract->stateVariables())
|
||||||
@ -419,8 +418,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
|||||||
auto functionType = make_shared<FunctionType>(*v);
|
auto functionType = make_shared<FunctionType>(*v);
|
||||||
// under non error circumstances this should be true
|
// under non error circumstances this should be true
|
||||||
if (functionType->interfaceFunctionType())
|
if (functionType->interfaceFunctionType())
|
||||||
externalDeclarations[functionType->externalSignature()].push_back(
|
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||||
make_pair(v, functionType->asCallableFunction(false))
|
v, functionType->asCallableFunction(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
|
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
|
||||||
|
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
#include <boost/range/algorithm/sort.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
@ -33,131 +35,112 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
|
|||||||
if (_function.isImplemented())
|
if (_function.isImplemented())
|
||||||
{
|
{
|
||||||
auto const& functionFlow = m_cfg.functionFlow(_function);
|
auto const& functionFlow = m_cfg.functionFlow(_function);
|
||||||
checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit);
|
checkUninitializedAccess(functionFlow.entry, functionFlow.exit);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
set<VariableDeclaration const*> ControlFlowAnalyzer::variablesAssignedInNode(CFGNode const *node)
|
void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const
|
||||||
{
|
{
|
||||||
set<VariableDeclaration const*> result;
|
struct NodeInfo
|
||||||
for (auto expression: node->block.expressions)
|
|
||||||
{
|
{
|
||||||
if (auto const* assignment = dynamic_cast<Assignment const*>(expression))
|
set<VariableDeclaration const*> unassignedVariablesAtEntry;
|
||||||
|
set<VariableDeclaration const*> unassignedVariablesAtExit;
|
||||||
|
set<VariableOccurrence const*> uninitializedVariableAccesses;
|
||||||
|
/// Propagate the information from another node to this node.
|
||||||
|
/// To be used to propagate information from a node to its exit nodes.
|
||||||
|
/// Returns true, if new variables were added and thus the current node has
|
||||||
|
/// to be traversed again.
|
||||||
|
bool propagateFrom(NodeInfo const& _entryNode)
|
||||||
{
|
{
|
||||||
stack<Expression const*> expressions;
|
size_t previousUnassignedVariablesAtEntry = unassignedVariablesAtEntry.size();
|
||||||
expressions.push(&assignment->leftHandSide());
|
size_t previousUninitializedVariableAccessess = uninitializedVariableAccesses.size();
|
||||||
while (!expressions.empty())
|
unassignedVariablesAtEntry += _entryNode.unassignedVariablesAtExit;
|
||||||
{
|
uninitializedVariableAccesses += _entryNode.uninitializedVariableAccesses;
|
||||||
Expression const* expression = expressions.top();
|
return
|
||||||
expressions.pop();
|
unassignedVariablesAtEntry.size() > previousUnassignedVariablesAtEntry ||
|
||||||
|
uninitializedVariableAccesses.size() > previousUninitializedVariableAccessess
|
||||||
if (auto const *tuple = dynamic_cast<TupleExpression const*>(expression))
|
;
|
||||||
for (auto const& component: tuple->components())
|
|
||||||
expressions.push(component.get());
|
|
||||||
else if (auto const* identifier = dynamic_cast<Identifier const*>(expression))
|
|
||||||
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(
|
|
||||||
identifier->annotation().referencedDeclaration
|
|
||||||
))
|
|
||||||
result.insert(variableDeclaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
map<CFGNode const*, NodeInfo> nodeInfos;
|
||||||
|
set<CFGNode const*> nodesToTraverse;
|
||||||
|
nodesToTraverse.insert(_entry);
|
||||||
|
|
||||||
void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
|
// Walk all paths starting from the nodes in ``nodesToTraverse`` until ``NodeInfo::propagateFrom``
|
||||||
FunctionDefinition const& _function,
|
// returns false for all exits, i.e. until all paths have been walked with maximal sets of unassigned
|
||||||
CFGNode const* _functionEntry,
|
// variables and accesses.
|
||||||
CFGNode const* _functionExit
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
if (_function.returnParameterList()->parameters().empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
map<CFGNode const*, set<VariableDeclaration const*>> unassigned;
|
|
||||||
|
|
||||||
{
|
|
||||||
auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
|
|
||||||
for (auto const& returnParameter: _function.returnParameterList()->parameters())
|
|
||||||
if (
|
|
||||||
returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
|
|
||||||
returnParameter->type()->category() == Type::Category::Mapping
|
|
||||||
)
|
|
||||||
unassignedAtFunctionEntry.insert(returnParameter.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
stack<CFGNode const*> nodesToTraverse;
|
|
||||||
nodesToTraverse.push(_functionEntry);
|
|
||||||
|
|
||||||
// walk all paths from entry with maximal set of unassigned return values
|
|
||||||
while (!nodesToTraverse.empty())
|
while (!nodesToTraverse.empty())
|
||||||
{
|
{
|
||||||
auto node = nodesToTraverse.top();
|
CFGNode const* currentNode = *nodesToTraverse.begin();
|
||||||
nodesToTraverse.pop();
|
nodesToTraverse.erase(nodesToTraverse.begin());
|
||||||
|
|
||||||
auto& unassignedAtNode = unassigned[node];
|
auto& nodeInfo = nodeInfos[currentNode];
|
||||||
|
auto unassignedVariables = nodeInfo.unassignedVariablesAtEntry;
|
||||||
if (node->block.returnStatement != nullptr)
|
for (auto const& variableOccurrence: currentNode->variableOccurrences)
|
||||||
if (node->block.returnStatement->expression())
|
|
||||||
unassignedAtNode.clear();
|
|
||||||
if (!unassignedAtNode.empty())
|
|
||||||
{
|
{
|
||||||
// kill all return values to which a value is assigned
|
switch (variableOccurrence.kind())
|
||||||
for (auto const* variableDeclaration: variablesAssignedInNode(node))
|
{
|
||||||
unassignedAtNode.erase(variableDeclaration);
|
case VariableOccurrence::Kind::Assignment:
|
||||||
|
unassignedVariables.erase(&variableOccurrence.declaration());
|
||||||
// kill all return values referenced in inline assembly
|
break;
|
||||||
// a reference is enough, checking whether there actually was an assignment might be overkill
|
case VariableOccurrence::Kind::InlineAssembly:
|
||||||
for (auto assembly: node->block.inlineAssemblyStatements)
|
// We consider all variables referenced in inline assembly as accessed.
|
||||||
for (auto const& ref: assembly->annotation().externalReferences)
|
// So far any reference is enough, but we might want to actually analyze
|
||||||
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
|
// the control flow in the assembly at some point.
|
||||||
unassignedAtNode.erase(variableDeclaration);
|
case VariableOccurrence::Kind::Access:
|
||||||
|
case VariableOccurrence::Kind::Return:
|
||||||
|
if (unassignedVariables.count(&variableOccurrence.declaration()))
|
||||||
|
{
|
||||||
|
if (variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage))
|
||||||
|
// Merely store the unassigned access. We do not generate an error right away, since this
|
||||||
|
// path might still always revert. It is only an error if this is propagated to the exit
|
||||||
|
// node of the function (i.e. there is a path with an uninitialized access).
|
||||||
|
nodeInfo.uninitializedVariableAccesses.insert(&variableOccurrence);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
for (auto const& exit: node->exits)
|
case VariableOccurrence::Kind::Declaration:
|
||||||
{
|
unassignedVariables.insert(&variableOccurrence.declaration());
|
||||||
auto& unassignedAtExit = unassigned[exit];
|
break;
|
||||||
auto oldSize = unassignedAtExit.size();
|
|
||||||
unassignedAtExit.insert(unassignedAtNode.begin(), unassignedAtNode.end());
|
|
||||||
// (re)traverse an exit, if we are on a path with new unassigned return values to consider
|
|
||||||
// this will terminate, since there is only a finite number of unassigned return values
|
|
||||||
if (unassignedAtExit.size() > oldSize)
|
|
||||||
nodesToTraverse.push(exit);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nodeInfo.unassignedVariablesAtExit = std::move(unassignedVariables);
|
||||||
|
|
||||||
if (!unassigned[_functionExit].empty())
|
// Propagate changes to all exits and queue them for traversal, if needed.
|
||||||
|
for (auto const& exit: currentNode->exits)
|
||||||
|
if (nodeInfos[exit].propagateFrom(nodeInfo))
|
||||||
|
nodesToTraverse.insert(exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& exitInfo = nodeInfos[_exit];
|
||||||
|
if (!exitInfo.uninitializedVariableAccesses.empty())
|
||||||
{
|
{
|
||||||
vector<VariableDeclaration const*> unassignedOrdered(
|
vector<VariableOccurrence const*> uninitializedAccessesOrdered(
|
||||||
unassigned[_functionExit].begin(),
|
exitInfo.uninitializedVariableAccesses.begin(),
|
||||||
unassigned[_functionExit].end()
|
exitInfo.uninitializedVariableAccesses.end()
|
||||||
);
|
);
|
||||||
sort(
|
boost::range::sort(
|
||||||
unassignedOrdered.begin(),
|
uninitializedAccessesOrdered,
|
||||||
unassignedOrdered.end(),
|
[](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool
|
||||||
[](VariableDeclaration const* lhs, VariableDeclaration const* rhs) -> bool {
|
{
|
||||||
return lhs->id() < rhs->id();
|
return *lhs < *rhs;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
for (auto const* returnVal: unassignedOrdered)
|
|
||||||
|
for (auto const* variableOccurrence: uninitializedAccessesOrdered)
|
||||||
{
|
{
|
||||||
SecondarySourceLocation ssl;
|
SecondarySourceLocation ssl;
|
||||||
for (CFGNode* lastNodeBeforeExit: _functionExit->entries)
|
if (variableOccurrence->occurrence())
|
||||||
if (unassigned[lastNodeBeforeExit].count(returnVal))
|
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||||
{
|
|
||||||
if (!!lastNodeBeforeExit->block.returnStatement)
|
|
||||||
ssl.append("Problematic return:", lastNodeBeforeExit->block.returnStatement->location());
|
|
||||||
else
|
|
||||||
ssl.append("Problematic end of function:", _function.location());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
returnVal->location(),
|
variableOccurrence->occurrence() ?
|
||||||
|
variableOccurrence->occurrence()->location() :
|
||||||
|
variableOccurrence->declaration().location(),
|
||||||
ssl,
|
ssl,
|
||||||
"This variable is of storage pointer type and might be returned without assignment and "
|
string("This variable is of storage pointer type and can be ") +
|
||||||
"could be used uninitialized. Assign the variable (potentially from itself) "
|
(variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
|
||||||
"to fix this error."
|
" without prior assignment."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -37,12 +36,8 @@ public:
|
|||||||
bool visit(FunctionDefinition const& _function) override;
|
bool visit(FunctionDefinition const& _function) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
|
/// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
|
||||||
void checkUnassignedStorageReturnValues(
|
void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const;
|
||||||
FunctionDefinition const& _function,
|
|
||||||
CFGNode const* _functionEntry,
|
|
||||||
CFGNode const* _functionExit
|
|
||||||
) const;
|
|
||||||
|
|
||||||
CFG const& m_cfg;
|
CFG const& m_cfg;
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
@ -22,7 +22,10 @@ using namespace solidity;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
|
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
|
||||||
m_nodeContainer(_nodeContainer), m_currentFunctionFlow(_functionFlow), m_currentNode(_functionFlow.entry)
|
m_nodeContainer(_nodeContainer),
|
||||||
|
m_currentNode(_functionFlow.entry),
|
||||||
|
m_returnNode(_functionFlow.exit),
|
||||||
|
m_revertNode(_functionFlow.revert)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,28 +40,10 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
|||||||
functionFlow->revert = _nodeContainer.newNode();
|
functionFlow->revert = _nodeContainer.newNode();
|
||||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
||||||
builder.appendControlFlow(_function);
|
builder.appendControlFlow(_function);
|
||||||
connect(builder.m_currentNode, functionFlow->exit);
|
|
||||||
return functionFlow;
|
return functionFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unique_ptr<ModifierFlow> ControlFlowBuilder::createModifierFlow(
|
|
||||||
CFG::NodeContainer& _nodeContainer,
|
|
||||||
ModifierDefinition const& _modifier
|
|
||||||
)
|
|
||||||
{
|
|
||||||
auto modifierFlow = unique_ptr<ModifierFlow>(new ModifierFlow());
|
|
||||||
modifierFlow->entry = _nodeContainer.newNode();
|
|
||||||
modifierFlow->exit = _nodeContainer.newNode();
|
|
||||||
modifierFlow->revert = _nodeContainer.newNode();
|
|
||||||
modifierFlow->placeholderEntry = _nodeContainer.newNode();
|
|
||||||
modifierFlow->placeholderExit = _nodeContainer.newNode();
|
|
||||||
ControlFlowBuilder builder(_nodeContainer, *modifierFlow);
|
|
||||||
builder.appendControlFlow(_modifier);
|
|
||||||
connect(builder.m_currentNode, modifierFlow->exit);
|
|
||||||
return modifierFlow;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
|
bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentNode, "");
|
solAssert(!!m_currentNode, "");
|
||||||
@ -219,64 +204,24 @@ bool ControlFlowBuilder::visit(Continue const&)
|
|||||||
bool ControlFlowBuilder::visit(Throw const&)
|
bool ControlFlowBuilder::visit(Throw const&)
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentNode, "");
|
solAssert(!!m_currentNode, "");
|
||||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
solAssert(!!m_revertNode, "");
|
||||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
connect(m_currentNode, m_revertNode);
|
||||||
m_currentNode = newLabel();
|
m_currentNode = newLabel();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ControlFlowBuilder::visit(Block const&)
|
|
||||||
{
|
|
||||||
solAssert(!!m_currentNode, "");
|
|
||||||
createLabelHere();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControlFlowBuilder::endVisit(Block const&)
|
|
||||||
{
|
|
||||||
solAssert(!!m_currentNode, "");
|
|
||||||
createLabelHere();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ControlFlowBuilder::visit(Return const& _return)
|
|
||||||
{
|
|
||||||
solAssert(!!m_currentNode, "");
|
|
||||||
solAssert(!!m_currentFunctionFlow.exit, "");
|
|
||||||
solAssert(!m_currentNode->block.returnStatement, "");
|
|
||||||
m_currentNode->block.returnStatement = &_return;
|
|
||||||
connect(m_currentNode, m_currentFunctionFlow.exit);
|
|
||||||
m_currentNode = newLabel();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
|
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentNode, "");
|
solAssert(!!m_currentNode, "");
|
||||||
auto modifierFlow = dynamic_cast<ModifierFlow const*>(&m_currentFunctionFlow);
|
solAssert(!!m_placeholderEntry, "");
|
||||||
solAssert(!!modifierFlow, "");
|
solAssert(!!m_placeholderExit, "");
|
||||||
|
|
||||||
connect(m_currentNode, modifierFlow->placeholderEntry);
|
|
||||||
|
|
||||||
|
connect(m_currentNode, m_placeholderEntry);
|
||||||
m_currentNode = newLabel();
|
m_currentNode = newLabel();
|
||||||
|
connect(m_placeholderExit, m_currentNode);
|
||||||
connect(modifierFlow->placeholderExit, m_currentNode);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ControlFlowBuilder::visitNode(ASTNode const& node)
|
|
||||||
{
|
|
||||||
solAssert(!!m_currentNode, "");
|
|
||||||
if (auto const* expression = dynamic_cast<Expression const*>(&node))
|
|
||||||
m_currentNode->block.expressions.emplace_back(expression);
|
|
||||||
else if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(&node))
|
|
||||||
m_currentNode->block.variableDeclarations.emplace_back(variableDeclaration);
|
|
||||||
else if (auto const* assembly = dynamic_cast<InlineAssembly const*>(&node))
|
|
||||||
m_currentNode->block.inlineAssemblyStatements.emplace_back(assembly);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentNode, "");
|
solAssert(!!m_currentNode, "");
|
||||||
@ -286,19 +231,19 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
|||||||
switch (functionType->kind())
|
switch (functionType->kind())
|
||||||
{
|
{
|
||||||
case FunctionType::Kind::Revert:
|
case FunctionType::Kind::Revert:
|
||||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
solAssert(!!m_revertNode, "");
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
connect(m_currentNode, m_revertNode);
|
||||||
m_currentNode = newLabel();
|
m_currentNode = newLabel();
|
||||||
return false;
|
return false;
|
||||||
case FunctionType::Kind::Require:
|
case FunctionType::Kind::Require:
|
||||||
case FunctionType::Kind::Assert:
|
case FunctionType::Kind::Assert:
|
||||||
{
|
{
|
||||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
solAssert(!!m_revertNode, "");
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
connect(m_currentNode, m_revertNode);
|
||||||
auto nextNode = newLabel();
|
auto nextNode = newLabel();
|
||||||
connect(m_currentNode, nextNode);
|
connect(m_currentNode, nextNode);
|
||||||
m_currentNode = nextNode;
|
m_currentNode = nextNode;
|
||||||
@ -310,6 +255,183 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
|||||||
return ASTConstVisitor::visit(_functionCall);
|
return ASTConstVisitor::visit(_functionCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
||||||
|
{
|
||||||
|
if (auto arguments = _modifierInvocation.arguments())
|
||||||
|
for (auto& argument: *arguments)
|
||||||
|
appendControlFlow(*argument);
|
||||||
|
|
||||||
|
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
||||||
|
_modifierInvocation.name()->annotation().referencedDeclaration
|
||||||
|
);
|
||||||
|
if (!modifierDefinition) return false;
|
||||||
|
solAssert(!!modifierDefinition, "");
|
||||||
|
solAssert(!!m_returnNode, "");
|
||||||
|
|
||||||
|
m_placeholderEntry = newLabel();
|
||||||
|
m_placeholderExit = newLabel();
|
||||||
|
|
||||||
|
appendControlFlow(*modifierDefinition);
|
||||||
|
connect(m_currentNode, m_returnNode);
|
||||||
|
|
||||||
|
m_currentNode = m_placeholderEntry;
|
||||||
|
m_returnNode = m_placeholderExit;
|
||||||
|
|
||||||
|
m_placeholderEntry = nullptr;
|
||||||
|
m_placeholderExit = nullptr;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
|
||||||
|
{
|
||||||
|
for (auto const& parameter: _functionDefinition.parameters())
|
||||||
|
appendControlFlow(*parameter);
|
||||||
|
|
||||||
|
for (auto const& returnParameter: _functionDefinition.returnParameters())
|
||||||
|
{
|
||||||
|
appendControlFlow(*returnParameter);
|
||||||
|
m_returnNode->variableOccurrences.emplace_back(
|
||||||
|
*returnParameter,
|
||||||
|
VariableOccurrence::Kind::Return,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& modifier: _functionDefinition.modifiers())
|
||||||
|
appendControlFlow(*modifier);
|
||||||
|
|
||||||
|
appendControlFlow(_functionDefinition.body());
|
||||||
|
|
||||||
|
connect(m_currentNode, m_returnNode);
|
||||||
|
m_currentNode = nullptr;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(Return const& _return)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentNode, "");
|
||||||
|
solAssert(!!m_returnNode, "");
|
||||||
|
if (_return.expression())
|
||||||
|
{
|
||||||
|
appendControlFlow(*_return.expression());
|
||||||
|
// Returns with return expression are considered to be assignments to the return parameters.
|
||||||
|
for (auto returnParameter: _return.annotation().functionReturnParameters->parameters())
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
*returnParameter,
|
||||||
|
VariableOccurrence::Kind::Assignment,
|
||||||
|
&_return
|
||||||
|
);
|
||||||
|
}
|
||||||
|
connect(m_currentNode, m_returnNode);
|
||||||
|
m_currentNode = newLabel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(FunctionTypeName const&)
|
||||||
|
{
|
||||||
|
// Do not visit the parameters and return values of a function type name.
|
||||||
|
// We do not want to consider them as variable declarations for the control flow graph.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentNode, "");
|
||||||
|
for (auto const& ref: _inlineAssembly.annotation().externalReferences)
|
||||||
|
{
|
||||||
|
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
*variableDeclaration,
|
||||||
|
VariableOccurrence::Kind::InlineAssembly,
|
||||||
|
&_inlineAssembly
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentNode, "");
|
||||||
|
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
_variableDeclaration,
|
||||||
|
VariableOccurrence::Kind::Declaration,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle declaration with immediate assignment.
|
||||||
|
if (_variableDeclaration.value())
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
_variableDeclaration,
|
||||||
|
VariableOccurrence::Kind::Assignment,
|
||||||
|
_variableDeclaration.value().get()
|
||||||
|
);
|
||||||
|
// Function arguments are considered to be immediately assigned as well (they are "externally assigned").
|
||||||
|
else if (_variableDeclaration.isCallableParameter() && !_variableDeclaration.isReturnParameter())
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
_variableDeclaration,
|
||||||
|
VariableOccurrence::Kind::Assignment,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentNode, "");
|
||||||
|
|
||||||
|
for (auto const& var: _variableDeclarationStatement.declarations())
|
||||||
|
if (var)
|
||||||
|
var->accept(*this);
|
||||||
|
if (_variableDeclarationStatement.initialValue())
|
||||||
|
{
|
||||||
|
_variableDeclarationStatement.initialValue()->accept(*this);
|
||||||
|
for (size_t i = 0; i < _variableDeclarationStatement.declarations().size(); i++)
|
||||||
|
if (auto const& var = _variableDeclarationStatement.declarations()[i])
|
||||||
|
{
|
||||||
|
auto expression = _variableDeclarationStatement.initialValue();
|
||||||
|
if (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
|
||||||
|
if (tupleExpression->components().size() > 1)
|
||||||
|
{
|
||||||
|
solAssert(tupleExpression->components().size() > i, "");
|
||||||
|
expression = tupleExpression->components()[i].get();
|
||||||
|
}
|
||||||
|
while (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
|
||||||
|
if (tupleExpression->components().size() == 1)
|
||||||
|
expression = tupleExpression->components().front().get();
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
*var,
|
||||||
|
VariableOccurrence::Kind::Assignment,
|
||||||
|
expression
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowBuilder::visit(Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentNode, "");
|
||||||
|
|
||||||
|
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||||
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
|
*variableDeclaration,
|
||||||
|
static_cast<Expression const&>(_identifier).annotation().lValueRequested ?
|
||||||
|
VariableOccurrence::Kind::Assignment :
|
||||||
|
VariableOccurrence::Kind::Access,
|
||||||
|
&_identifier
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
|
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
|
||||||
{
|
{
|
||||||
_node.accept(*this);
|
_node.accept(*this);
|
||||||
|
@ -38,14 +38,11 @@ public:
|
|||||||
CFG::NodeContainer& _nodeContainer,
|
CFG::NodeContainer& _nodeContainer,
|
||||||
FunctionDefinition const& _function
|
FunctionDefinition const& _function
|
||||||
);
|
);
|
||||||
static std::unique_ptr<ModifierFlow> createModifierFlow(
|
|
||||||
CFG::NodeContainer& _nodeContainer,
|
|
||||||
ModifierDefinition const& _modifier
|
|
||||||
);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
|
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
|
||||||
|
|
||||||
|
// Visits for constructing the control flow.
|
||||||
bool visit(BinaryOperation const& _operation) override;
|
bool visit(BinaryOperation const& _operation) override;
|
||||||
bool visit(Conditional const& _conditional) override;
|
bool visit(Conditional const& _conditional) override;
|
||||||
bool visit(IfStatement const& _ifStatement) override;
|
bool visit(IfStatement const& _ifStatement) override;
|
||||||
@ -54,12 +51,20 @@ private:
|
|||||||
bool visit(Break const&) override;
|
bool visit(Break const&) override;
|
||||||
bool visit(Continue const&) override;
|
bool visit(Continue const&) override;
|
||||||
bool visit(Throw const&) override;
|
bool visit(Throw const&) override;
|
||||||
bool visit(Block const&) override;
|
|
||||||
void endVisit(Block const&) override;
|
|
||||||
bool visit(Return const& _return) override;
|
|
||||||
bool visit(PlaceholderStatement const&) override;
|
bool visit(PlaceholderStatement const&) override;
|
||||||
bool visit(FunctionCall const& _functionCall) override;
|
bool visit(FunctionCall const& _functionCall) override;
|
||||||
|
bool visit(ModifierInvocation const& _modifierInvocation) override;
|
||||||
|
|
||||||
|
// Visits for constructing the control flow as well as filling variable occurrences.
|
||||||
|
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||||
|
bool visit(Return const& _return) override;
|
||||||
|
|
||||||
|
// Visits for filling variable occurrences.
|
||||||
|
bool visit(FunctionTypeName const& _functionTypeName) override;
|
||||||
|
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
|
bool visit(VariableDeclaration const& _variableDeclaration) override;
|
||||||
|
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||||
|
bool visit(Identifier const& _identifier) override;
|
||||||
|
|
||||||
/// Appends the control flow of @a _node to the current control flow.
|
/// Appends the control flow of @a _node to the current control flow.
|
||||||
void appendControlFlow(ASTNode const& _node);
|
void appendControlFlow(ASTNode const& _node);
|
||||||
@ -73,9 +78,6 @@ private:
|
|||||||
static void connect(CFGNode* _from, CFGNode* _to);
|
static void connect(CFGNode* _from, CFGNode* _to);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool visitNode(ASTNode const& node) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// Splits the control flow starting at the current node into n paths.
|
/// Splits the control flow starting at the current node into n paths.
|
||||||
@ -114,17 +116,18 @@ private:
|
|||||||
|
|
||||||
CFG::NodeContainer& m_nodeContainer;
|
CFG::NodeContainer& m_nodeContainer;
|
||||||
|
|
||||||
/// The control flow of the function that is currently parsed.
|
|
||||||
/// Note: this can also be a ModifierFlow
|
|
||||||
FunctionFlow const& m_currentFunctionFlow;
|
|
||||||
|
|
||||||
CFGNode* m_currentNode = nullptr;
|
CFGNode* m_currentNode = nullptr;
|
||||||
|
CFGNode* m_returnNode = nullptr;
|
||||||
|
CFGNode* m_revertNode = nullptr;
|
||||||
|
|
||||||
/// The current jump destination of break Statements.
|
/// The current jump destination of break Statements.
|
||||||
CFGNode* m_breakJump = nullptr;
|
CFGNode* m_breakJump = nullptr;
|
||||||
/// The current jump destination of continue Statements.
|
/// The current jump destination of continue Statements.
|
||||||
CFGNode* m_continueJump = nullptr;
|
CFGNode* m_continueJump = nullptr;
|
||||||
|
|
||||||
|
CFGNode* m_placeholderEntry = nullptr;
|
||||||
|
CFGNode* m_placeholderExit = nullptr;
|
||||||
|
|
||||||
/// Helper class that replaces the break and continue jump destinations for the
|
/// Helper class that replaces the break and continue jump destinations for the
|
||||||
/// current scope and restores the originals at the end of the scope.
|
/// current scope and restores the originals at the end of the scope.
|
||||||
class BreakContinueScope
|
class BreakContinueScope
|
||||||
|
@ -16,10 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||||
|
|
||||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -29,19 +28,13 @@ using namespace dev::solidity;
|
|||||||
bool CFG::constructFlow(ASTNode const& _astRoot)
|
bool CFG::constructFlow(ASTNode const& _astRoot)
|
||||||
{
|
{
|
||||||
_astRoot.accept(*this);
|
_astRoot.accept(*this);
|
||||||
applyModifiers();
|
|
||||||
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CFG::visit(ModifierDefinition const& _modifier)
|
|
||||||
{
|
|
||||||
m_modifierControlFlow[&_modifier] = ControlFlowBuilder::createModifierFlow(m_nodeContainer, _modifier);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CFG::visit(FunctionDefinition const& _function)
|
bool CFG::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
|
if (_function.isImplemented())
|
||||||
m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -57,81 +50,3 @@ CFGNode* CFG::NodeContainer::newNode()
|
|||||||
m_nodes.emplace_back(new CFGNode());
|
m_nodes.emplace_back(new CFGNode());
|
||||||
return m_nodes.back().get();
|
return m_nodes.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFG::applyModifiers()
|
|
||||||
{
|
|
||||||
for (auto const& function: m_functionControlFlow)
|
|
||||||
{
|
|
||||||
for (auto const& modifierInvocation: boost::adaptors::reverse(function.first->modifiers()))
|
|
||||||
{
|
|
||||||
if (auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
|
||||||
modifierInvocation->name()->annotation().referencedDeclaration
|
|
||||||
))
|
|
||||||
{
|
|
||||||
solAssert(m_modifierControlFlow.count(modifierDefinition), "");
|
|
||||||
applyModifierFlowToFunctionFlow(*m_modifierControlFlow[modifierDefinition], function.second.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CFG::applyModifierFlowToFunctionFlow(
|
|
||||||
ModifierFlow const& _modifierFlow,
|
|
||||||
FunctionFlow* _functionFlow
|
|
||||||
)
|
|
||||||
{
|
|
||||||
solAssert(!!_functionFlow, "");
|
|
||||||
|
|
||||||
map<CFGNode*, CFGNode*> copySrcToCopyDst;
|
|
||||||
|
|
||||||
// inherit the revert node of the function
|
|
||||||
copySrcToCopyDst[_modifierFlow.revert] = _functionFlow->revert;
|
|
||||||
|
|
||||||
// replace the placeholder nodes by the function entry and exit
|
|
||||||
copySrcToCopyDst[_modifierFlow.placeholderEntry] = _functionFlow->entry;
|
|
||||||
copySrcToCopyDst[_modifierFlow.placeholderExit] = _functionFlow->exit;
|
|
||||||
|
|
||||||
stack<CFGNode*> nodesToCopy;
|
|
||||||
nodesToCopy.push(_modifierFlow.entry);
|
|
||||||
|
|
||||||
// map the modifier entry to a new node that will become the new function entry
|
|
||||||
copySrcToCopyDst[_modifierFlow.entry] = m_nodeContainer.newNode();
|
|
||||||
|
|
||||||
while (!nodesToCopy.empty())
|
|
||||||
{
|
|
||||||
CFGNode* copySrcNode = nodesToCopy.top();
|
|
||||||
nodesToCopy.pop();
|
|
||||||
|
|
||||||
solAssert(copySrcToCopyDst.count(copySrcNode), "");
|
|
||||||
|
|
||||||
CFGNode* copyDstNode = copySrcToCopyDst[copySrcNode];
|
|
||||||
|
|
||||||
copyDstNode->block = copySrcNode->block;
|
|
||||||
for (auto const& entry: copySrcNode->entries)
|
|
||||||
{
|
|
||||||
if (!copySrcToCopyDst.count(entry))
|
|
||||||
{
|
|
||||||
copySrcToCopyDst[entry] = m_nodeContainer.newNode();
|
|
||||||
nodesToCopy.push(entry);
|
|
||||||
}
|
|
||||||
copyDstNode->entries.emplace_back(copySrcToCopyDst[entry]);
|
|
||||||
}
|
|
||||||
for (auto const& exit: copySrcNode->exits)
|
|
||||||
{
|
|
||||||
if (!copySrcToCopyDst.count(exit))
|
|
||||||
{
|
|
||||||
copySrcToCopyDst[exit] = m_nodeContainer.newNode();
|
|
||||||
nodesToCopy.push(exit);
|
|
||||||
}
|
|
||||||
copyDstNode->exits.emplace_back(copySrcToCopyDst[exit]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the modifier control flow never reached its exit node,
|
|
||||||
// we need to create a new (disconnected) exit node now
|
|
||||||
if (!copySrcToCopyDst.count(_modifierFlow.exit))
|
|
||||||
copySrcToCopyDst[_modifierFlow.exit] = m_nodeContainer.newNode();
|
|
||||||
|
|
||||||
_functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
|
|
||||||
_functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
|
|
||||||
}
|
|
||||||
|
@ -31,25 +31,57 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Basic Control Flow Block.
|
/** Occurrence of a variable in a block of control flow.
|
||||||
* Basic block of control flow. Consists of a set of (unordered) AST nodes
|
* Stores the declaration of the referenced variable, the
|
||||||
* for which control flow is always linear. A basic control flow block
|
* kind of the occurrence and possibly the node at which
|
||||||
* encompasses at most one scope. Reverts are considered to break the control
|
* it occurred.
|
||||||
* flow.
|
|
||||||
* @todo Handle function calls correctly. So far function calls are not considered
|
|
||||||
* to change the control flow.
|
|
||||||
*/
|
*/
|
||||||
struct ControlFlowBlock
|
class VariableOccurrence
|
||||||
{
|
{
|
||||||
/// All variable declarations inside this control flow block.
|
public:
|
||||||
std::vector<VariableDeclaration const*> variableDeclarations;
|
enum class Kind
|
||||||
/// All expressions inside this control flow block (this includes all subexpressions!).
|
{
|
||||||
std::vector<Expression const*> expressions;
|
Declaration,
|
||||||
/// All inline assembly statements inside in this control flow block.
|
Access,
|
||||||
std::vector<InlineAssembly const*> inlineAssemblyStatements;
|
Return,
|
||||||
/// If control flow returns in this node, the return statement is stored in returnStatement,
|
Assignment,
|
||||||
/// otherwise returnStatement is nullptr.
|
InlineAssembly
|
||||||
Return const* returnStatement = nullptr;
|
};
|
||||||
|
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, ASTNode const* _occurrence):
|
||||||
|
m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a deterministic order on variable occurrences.
|
||||||
|
bool operator<(VariableOccurrence const& _rhs) const
|
||||||
|
{
|
||||||
|
if (m_occurrence && _rhs.m_occurrence)
|
||||||
|
{
|
||||||
|
if (m_occurrence->id() < _rhs.m_occurrence->id()) return true;
|
||||||
|
if (_rhs.m_occurrence->id() < m_occurrence->id()) return false;
|
||||||
|
}
|
||||||
|
else if (_rhs.m_occurrence)
|
||||||
|
return true;
|
||||||
|
else if (m_occurrence)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using KindCompareType = std::underlying_type<VariableOccurrence::Kind>::type;
|
||||||
|
return
|
||||||
|
std::make_pair(m_declaration.id(), static_cast<KindCompareType>(m_occurrenceKind)) <
|
||||||
|
std::make_pair(_rhs.m_declaration.id(), static_cast<KindCompareType>(_rhs.m_occurrenceKind))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableDeclaration const& declaration() const { return m_declaration; }
|
||||||
|
Kind kind() const { return m_occurrenceKind; };
|
||||||
|
ASTNode const* occurrence() const { return m_occurrence; }
|
||||||
|
private:
|
||||||
|
/// Declaration of the occurring variable.
|
||||||
|
VariableDeclaration const& m_declaration;
|
||||||
|
/// Kind of occurrence.
|
||||||
|
Kind m_occurrenceKind = Kind::Access;
|
||||||
|
/// AST node at which the variable occurred, if available (may be nullptr).
|
||||||
|
ASTNode const* m_occurrence = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Node of the Control Flow Graph.
|
/** Node of the Control Flow Graph.
|
||||||
@ -64,14 +96,15 @@ struct CFGNode
|
|||||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||||
std::vector<CFGNode*> exits;
|
std::vector<CFGNode*> exits;
|
||||||
|
|
||||||
/// Control flow in the node.
|
/// Variable occurrences in the node.
|
||||||
ControlFlowBlock block;
|
std::vector<VariableOccurrence> variableOccurrences;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Describes the control flow of a function. */
|
/** Describes the control flow of a function. */
|
||||||
struct FunctionFlow
|
struct FunctionFlow
|
||||||
{
|
{
|
||||||
virtual ~FunctionFlow() {}
|
virtual ~FunctionFlow() = default;
|
||||||
|
|
||||||
/// Entry node. Control flow of the function starts here.
|
/// Entry node. Control flow of the function starts here.
|
||||||
/// This node is empty and does not have any entries.
|
/// This node is empty and does not have any entries.
|
||||||
CFGNode* entry = nullptr;
|
CFGNode* entry = nullptr;
|
||||||
@ -85,19 +118,6 @@ struct FunctionFlow
|
|||||||
CFGNode* revert = nullptr;
|
CFGNode* revert = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Describes the control flow of a modifier.
|
|
||||||
* Every placeholder breaks the control flow. The node preceding the
|
|
||||||
* placeholder is assigned placeholderEntry as exit and the node
|
|
||||||
* following the placeholder is assigned placeholderExit as entry.
|
|
||||||
*/
|
|
||||||
struct ModifierFlow: FunctionFlow
|
|
||||||
{
|
|
||||||
/// Control flow leading towards a placeholder exit in placeholderEntry.
|
|
||||||
CFGNode* placeholderEntry = nullptr;
|
|
||||||
/// Control flow coming from a placeholder enter from placeholderExit.
|
|
||||||
CFGNode* placeholderExit = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CFG: private ASTConstVisitor
|
class CFG: private ASTConstVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -105,7 +125,6 @@ public:
|
|||||||
|
|
||||||
bool constructFlow(ASTNode const& _astRoot);
|
bool constructFlow(ASTNode const& _astRoot);
|
||||||
|
|
||||||
bool visit(ModifierDefinition const& _modifier) override;
|
|
||||||
bool visit(FunctionDefinition const& _function) override;
|
bool visit(FunctionDefinition const& _function) override;
|
||||||
|
|
||||||
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
|
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
|
||||||
@ -118,20 +137,6 @@ public:
|
|||||||
std::vector<std::unique_ptr<CFGNode>> m_nodes;
|
std::vector<std::unique_ptr<CFGNode>> m_nodes;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
/// Initially the control flow for all functions *ignoring* modifiers and for
|
|
||||||
/// all modifiers is constructed. Afterwards the control flow of functions
|
|
||||||
/// is adjusted by applying all modifiers.
|
|
||||||
void applyModifiers();
|
|
||||||
|
|
||||||
/// Creates a copy of the modifier flow @a _modifierFlow, while replacing the
|
|
||||||
/// placeholder entry and exit with the function entry and exit, as well as
|
|
||||||
/// replacing the modifier revert node with the function's revert node.
|
|
||||||
/// The resulting control flow is the new function flow with the modifier applied.
|
|
||||||
/// @a _functionFlow is updated in-place.
|
|
||||||
void applyModifierFlowToFunctionFlow(
|
|
||||||
ModifierFlow const& _modifierFlow,
|
|
||||||
FunctionFlow* _functionFlow
|
|
||||||
);
|
|
||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
@ -141,7 +146,6 @@ private:
|
|||||||
NodeContainer m_nodeContainer;
|
NodeContainer m_nodeContainer;
|
||||||
|
|
||||||
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
|
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
|
||||||
std::map<ModifierDefinition const*, std::unique_ptr<ModifierFlow>> m_modifierControlFlow;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libdevcore/StringUtils.h>
|
#include <libdevcore/StringUtils.h>
|
||||||
|
@ -22,11 +22,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
@ -22,9 +22,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
|
||||||
#include <libsolidity/parsing/DocStringParser.h>
|
#include <libsolidity/parsing/DocStringParser.h>
|
||||||
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
|
@ -21,10 +21,11 @@
|
|||||||
* Container of the (implicit and explicit) global objects.
|
* Container of the (implicit and explicit) global objects.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <libsolidity/analysis/GlobalContext.h>
|
#include <libsolidity/analysis/GlobalContext.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <vector>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <string>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <vector>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
@ -22,11 +22,10 @@
|
|||||||
|
|
||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <libdevcore/StringUtils.h>
|
#include <libdevcore/StringUtils.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -22,13 +22,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
|
@ -16,15 +16,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/PostTypeChecker.h>
|
#include <libsolidity/analysis/PostTypeChecker.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
|
||||||
#include <libsolidity/analysis/SemVerHandler.h>
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
|
||||||
#include <libsolidity/interface/Version.h>
|
|
||||||
|
|
||||||
|
#include <libsolidity/analysis/SemVerHandler.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/interface/Version.h>
|
||||||
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <libdevcore/Algorithms.h>
|
#include <libdevcore/Algorithms.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/map.hpp>
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -21,12 +21,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
|
||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
#include <libyul/AsmAnalysis.h>
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
@ -316,7 +319,14 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
// We use the latest EVM version because we will re-run it anyway.
|
// We use the latest EVM version because we will re-run it anyway.
|
||||||
yul::AsmAnalysisInfo analysisInfo;
|
yul::AsmAnalysisInfo analysisInfo;
|
||||||
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
|
||||||
yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
yul::AsmAnalyzer(
|
||||||
|
analysisInfo,
|
||||||
|
errorsIgnored,
|
||||||
|
EVMVersion(),
|
||||||
|
errorTypeForLoose,
|
||||||
|
yul::EVMDialect::looseAssemblyForEVM(),
|
||||||
|
resolver
|
||||||
|
).analyze(_inlineAssembly.operations());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,12 +22,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/SemVerHandler.h>
|
#include <libsolidity/analysis/SemVerHandler.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/StaticAnalyzer.h>
|
#include <libsolidity/analysis/StaticAnalyzer.h>
|
||||||
|
|
||||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
@ -63,8 +64,7 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
||||||
{
|
{
|
||||||
m_currentFunction = nullptr;
|
if (m_currentFunction && !m_currentFunction->body().statements().empty())
|
||||||
m_constructor = false;
|
|
||||||
for (auto const& var: m_localVarUseCount)
|
for (auto const& var: m_localVarUseCount)
|
||||||
if (var.second == 0)
|
if (var.second == 0)
|
||||||
{
|
{
|
||||||
@ -76,8 +76,9 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
|||||||
else
|
else
|
||||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_localVarUseCount.clear();
|
m_localVarUseCount.clear();
|
||||||
|
m_constructor = false;
|
||||||
|
m_currentFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StaticAnalyzer::visit(Identifier const& _identifier)
|
bool StaticAnalyzer::visit(Identifier const& _identifier)
|
||||||
|
@ -16,15 +16,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <memory>
|
|
||||||
|
#include <libsolidity/analysis/SemVerHandler.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||||
#include <libsolidity/analysis/SemVerHandler.h>
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
|
||||||
|
|
||||||
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -111,7 +114,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||||
SemVerMatchExpressionParser parser(tokens, literals);
|
SemVerMatchExpressionParser parser(tokens, literals);
|
||||||
auto matchExpression = parser.parse();
|
auto matchExpression = parser.parse();
|
||||||
SemVerVersion currentVersion{string(VersionString)};
|
static SemVerVersion const currentVersion{string(VersionString)};
|
||||||
if (!matchExpression.matches(currentVersion))
|
if (!matchExpression.matches(currentVersion))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
_pragma.location(),
|
_pragma.location(),
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include <libyul/AsmAnalysis.h>
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
@ -33,8 +34,8 @@
|
|||||||
#include <libdevcore/StringUtils.h>
|
#include <libdevcore/StringUtils.h>
|
||||||
|
|
||||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -658,7 +659,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
m_errorReporter,
|
m_errorReporter,
|
||||||
m_evmVersion,
|
m_evmVersion,
|
||||||
Error::Type::SyntaxError,
|
Error::Type::SyntaxError,
|
||||||
yul::AsmFlavour::Loose,
|
yul::EVMDialect::looseAssemblyForEVM(),
|
||||||
identifierAccess
|
identifierAccess
|
||||||
);
|
);
|
||||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||||
@ -935,31 +936,33 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
var.accept(*this);
|
var.accept(*this);
|
||||||
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
|
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
|
||||||
{
|
{
|
||||||
|
auto errorMsg = "Type " +
|
||||||
|
valueComponentType->toString() +
|
||||||
|
" is not implicitly convertible to expected type " +
|
||||||
|
var.annotation().type->toString();
|
||||||
if (
|
if (
|
||||||
valueComponentType->category() == Type::Category::RationalNumber &&
|
valueComponentType->category() == Type::Category::RationalNumber &&
|
||||||
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
|
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
|
||||||
valueComponentType->mobileType()
|
valueComponentType->mobileType()
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
if (var.annotation().type->operator==(*valueComponentType->mobileType()))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Type " +
|
errorMsg + ", but it can be explicitly converted."
|
||||||
valueComponentType->toString() +
|
|
||||||
" is not implicitly convertible to expected type " +
|
|
||||||
var.annotation().type->toString() +
|
|
||||||
". Try converting to type " +
|
|
||||||
valueComponentType->mobileType()->toString() +
|
|
||||||
" or use an explicit conversion."
|
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Type " +
|
errorMsg +
|
||||||
valueComponentType->toString() +
|
". Try converting to type " +
|
||||||
" is not implicitly convertible to expected type " +
|
valueComponentType->mobileType()->toString() +
|
||||||
var.annotation().type->toString() +
|
" or use an explicit conversion."
|
||||||
"."
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
m_errorReporter.typeError(_statement.location(), errorMsg + ".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1252,7 +1255,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|||||||
{
|
{
|
||||||
TypePointer const& leftType = type(_operation.leftExpression());
|
TypePointer const& leftType = type(_operation.leftExpression());
|
||||||
TypePointer const& rightType = type(_operation.rightExpression());
|
TypePointer const& rightType = type(_operation.rightExpression());
|
||||||
TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
|
TypeResult result = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
|
||||||
|
TypePointer commonType = result.get();
|
||||||
if (!commonType)
|
if (!commonType)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
@ -1262,7 +1266,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|||||||
" not compatible with types " +
|
" not compatible with types " +
|
||||||
leftType->toString() +
|
leftType->toString() +
|
||||||
" and " +
|
" and " +
|
||||||
rightType->toString()
|
rightType->toString() +
|
||||||
|
(!result.message().empty() ? ". " + result.message() : "")
|
||||||
);
|
);
|
||||||
commonType = leftType;
|
commonType = leftType;
|
||||||
}
|
}
|
||||||
@ -2329,30 +2334,32 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
|||||||
_expression.accept(*this);
|
_expression.accept(*this);
|
||||||
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
|
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
|
||||||
{
|
{
|
||||||
|
auto errorMsg = "Type " +
|
||||||
|
type(_expression)->toString() +
|
||||||
|
" is not implicitly convertible to expected type " +
|
||||||
|
_expectedType.toString();
|
||||||
if (
|
if (
|
||||||
type(_expression)->category() == Type::Category::RationalNumber &&
|
type(_expression)->category() == Type::Category::RationalNumber &&
|
||||||
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
|
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
|
||||||
type(_expression)->mobileType()
|
type(_expression)->mobileType()
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
if (_expectedType.operator==(*type(_expression)->mobileType()))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Type " +
|
errorMsg + ", but it can be explicitly converted."
|
||||||
type(_expression)->toString() +
|
|
||||||
" is not implicitly convertible to expected type " +
|
|
||||||
_expectedType.toString() +
|
|
||||||
". Try converting to type " +
|
|
||||||
type(_expression)->mobileType()->toString() +
|
|
||||||
" or use an explicit conversion."
|
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Type " +
|
errorMsg +
|
||||||
type(_expression)->toString() +
|
". Try converting to type " +
|
||||||
" is not implicitly convertible to expected type " +
|
type(_expression)->mobileType()->toString() +
|
||||||
_expectedType.toString() +
|
" or use an explicit conversion."
|
||||||
"."
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_errorReporter.typeError(_expression.location(), errorMsg + ".");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/Types.h>
|
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
#include <libsolidity/ast/Types.h>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
|
@ -16,14 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/analysis/ViewPureChecker.h>
|
#include <libsolidity/analysis/ViewPureChecker.h>
|
||||||
|
|
||||||
#include <libevmasm/SemanticInformation.h>
|
|
||||||
|
|
||||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
#include <libevmasm/SemanticInformation.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -156,6 +152,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
|||||||
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
|
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
|
||||||
_funDef.stateMutability() != StateMutability::Payable &&
|
_funDef.stateMutability() != StateMutability::Payable &&
|
||||||
_funDef.isImplemented() &&
|
_funDef.isImplemented() &&
|
||||||
|
!_funDef.body().statements().empty() &&
|
||||||
!_funDef.isConstructor() &&
|
!_funDef.isConstructor() &&
|
||||||
!_funDef.isFallback() &&
|
!_funDef.isFallback() &&
|
||||||
!_funDef.annotation().superFunction
|
!_funDef.annotation().superFunction
|
||||||
|
@ -21,13 +21,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/ast/AST_accept.h>
|
#include <libsolidity/ast/AST_accept.h>
|
||||||
|
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libdevcore/Keccak256.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
|
|||||||
{
|
{
|
||||||
signaturesSeen.insert(functionSignature);
|
signaturesSeen.insert(functionSignature);
|
||||||
FixedHash<4> hash(dev::keccak256(functionSignature));
|
FixedHash<4> hash(dev::keccak256(functionSignature));
|
||||||
m_interfaceFunctionList->push_back(make_pair(hash, fun));
|
m_interfaceFunctionList->emplace_back(hash, fun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,24 +22,22 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include <libsolidity/parsing/Token.h>
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
#include <libsolidity/ast/ASTEnums.h>
|
#include <libsolidity/ast/ASTEnums.h>
|
||||||
|
#include <libsolidity/parsing/Token.h>
|
||||||
|
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
#include <libdevcore/FixedHash.h>
|
#include <libdevcore/FixedHash.h>
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace yul
|
namespace yul
|
||||||
{
|
{
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace yul
|
namespace yul
|
||||||
{
|
{
|
||||||
@ -46,7 +46,7 @@ using TypePointer = std::shared_ptr<Type const>;
|
|||||||
|
|
||||||
struct ASTAnnotation
|
struct ASTAnnotation
|
||||||
{
|
{
|
||||||
virtual ~ASTAnnotation() {}
|
virtual ~ASTAnnotation() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DocTag
|
struct DocTag
|
||||||
@ -57,7 +57,7 @@ struct DocTag
|
|||||||
|
|
||||||
struct DocumentedAnnotation
|
struct DocumentedAnnotation
|
||||||
{
|
{
|
||||||
virtual ~DocumentedAnnotation() {}
|
virtual ~DocumentedAnnotation() = default;
|
||||||
/// Mapping docstring tag name -> content.
|
/// Mapping docstring tag name -> content.
|
||||||
std::multimap<std::string, DocTag> docTags;
|
std::multimap<std::string, DocTag> docTags;
|
||||||
};
|
};
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Forward-declare all AST node types
|
// Forward-declare all AST node types
|
||||||
|
@ -20,11 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTJsonConverter.h>
|
#include <libsolidity/ast/ASTJsonConverter.h>
|
||||||
#include <boost/algorithm/string/join.hpp>
|
|
||||||
#include <libdevcore/UTF8.h>
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/AsmPrinter.h>
|
#include <libyul/AsmPrinter.h>
|
||||||
|
#include <libdevcore/UTF8.h>
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
@ -234,7 +235,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
|
|||||||
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
|
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
|
||||||
make_pair("scope", idOrNull(_node.scope()))
|
make_pair("scope", idOrNull(_node.scope()))
|
||||||
};
|
};
|
||||||
attributes.push_back(make_pair("unitAlias", _node.name()));
|
attributes.emplace_back("unitAlias", _node.name());
|
||||||
Json::Value symbolAliases(Json::arrayValue);
|
Json::Value symbolAliases(Json::arrayValue);
|
||||||
for (auto const& symbolAlias: _node.symbolAliases())
|
for (auto const& symbolAlias: _node.symbolAliases())
|
||||||
{
|
{
|
||||||
@ -244,7 +245,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
|
|||||||
tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue;
|
tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue;
|
||||||
symbolAliases.append(tuple);
|
symbolAliases.append(tuple);
|
||||||
}
|
}
|
||||||
attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases)));
|
attributes.emplace_back("symbolAliases", std::move(symbolAliases));
|
||||||
setJsonNode(_node, "ImportDirective", std::move(attributes));
|
setJsonNode(_node, "ImportDirective", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -357,7 +358,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
|
|||||||
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
|
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
|
||||||
};
|
};
|
||||||
if (m_inEvent)
|
if (m_inEvent)
|
||||||
attributes.push_back(make_pair("indexed", _node.isIndexed()));
|
attributes.emplace_back("indexed", _node.isIndexed());
|
||||||
setJsonNode(_node, "VariableDeclaration", std::move(attributes));
|
setJsonNode(_node, "VariableDeclaration", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -647,11 +648,11 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
|
|||||||
};
|
};
|
||||||
if (m_legacy)
|
if (m_legacy)
|
||||||
{
|
{
|
||||||
attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall));
|
attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
|
||||||
attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion));
|
attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind)));
|
attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
|
||||||
appendExpressionAttributes(attributes, _node.annotation());
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
@ -724,7 +725,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
|
|||||||
std::vector<pair<string, Json::Value>> attributes = {
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
|
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
|
||||||
make_pair("value", value),
|
make_pair("value", value),
|
||||||
make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())),
|
make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(asBytes(_node.value()))),
|
||||||
make_pair(
|
make_pair(
|
||||||
"subdenomination",
|
"subdenomination",
|
||||||
subdenomination == Token::Illegal ?
|
subdenomination == Token::Illegal ?
|
||||||
|
@ -22,12 +22,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostream>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
#include <stack>
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
#include <ostream>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
|
@ -21,11 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTPrinter.h>
|
#include <libsolidity/ast/ASTPrinter.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
|
@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/interface/GasEstimator.h>
|
#include <libsolidity/interface/GasEstimator.h>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
@ -24,22 +24,22 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/Algorithms.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libdevcore/Keccak256.h>
|
||||||
#include <libdevcore/UTF8.h>
|
#include <libdevcore/UTF8.h>
|
||||||
#include <libdevcore/Algorithms.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <boost/range/algorithm/copy.hpp>
|
|
||||||
#include <boost/range/adaptor/sliced.hpp>
|
#include <boost/range/adaptor/sliced.hpp>
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/range/algorithm/copy.hpp>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@ -125,6 +125,22 @@ bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
|
|||||||
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
|
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether _value fits into IntegerType _type.
|
||||||
|
bool fitsIntegerType(bigint const& _value, IntegerType const& _type)
|
||||||
|
{
|
||||||
|
return (_type.minValue() <= _value) && (_value <= _type.maxValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether _value fits into _bits bits when having 1 bit as the sign bit
|
||||||
|
/// if _signed is true.
|
||||||
|
bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed)
|
||||||
|
{
|
||||||
|
return fitsIntegerType(_value, IntegerType(
|
||||||
|
_bits,
|
||||||
|
_signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
||||||
@ -446,7 +462,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
|
|||||||
continue;
|
continue;
|
||||||
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
|
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
|
||||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||||
members.push_back(MemberList::Member(function->name(), fun, function));
|
members.emplace_back(function->name(), fun, function);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
@ -466,7 +482,7 @@ string AddressType::richIdentifier() const
|
|||||||
return "t_address";
|
return "t_address";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.category() != category())
|
if (_other.category() != category())
|
||||||
return false;
|
return false;
|
||||||
@ -475,7 +491,7 @@ bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
|||||||
return other.m_stateMutability <= m_stateMutability;
|
return other.m_stateMutability <= m_stateMutability;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
|
if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
|
||||||
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
|
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
|
||||||
@ -504,17 +520,16 @@ u256 AddressType::literalValue(Literal const* _literal) const
|
|||||||
return u256(_literal->valueWithoutUnderscores());
|
return u256(_literal->valueWithoutUnderscores());
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer AddressType::unaryOperatorResult(Token _operator) const
|
TypeResult AddressType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
// Addresses can only be compared.
|
|
||||||
if (!TokenTraits::isCompareOp(_operator))
|
if (!TokenTraits::isCompareOp(_operator))
|
||||||
return TypePointer();
|
return TypeResult{"Arithmetic operations on addresses are not supported. Convert to integer first before using them."};
|
||||||
|
|
||||||
return Type::commonType(shared_from_this(), _other);
|
return Type::commonType(shared_from_this(), _other);
|
||||||
}
|
}
|
||||||
@ -576,7 +591,7 @@ string IntegerType::richIdentifier() const
|
|||||||
return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
|
return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() == category())
|
if (_convertTo.category() == category())
|
||||||
{
|
{
|
||||||
@ -597,7 +612,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return _convertTo.category() == category() ||
|
return _convertTo.category() == category() ||
|
||||||
_convertTo.category() == Category::Address ||
|
_convertTo.category() == Category::Address ||
|
||||||
@ -607,18 +622,17 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
_convertTo.category() == Category::FixedPoint;
|
_convertTo.category() == Category::FixedPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer IntegerType::unaryOperatorResult(Token _operator) const
|
TypeResult IntegerType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
// "delete" is ok for all integer types
|
// "delete" is ok for all integer types
|
||||||
if (_operator == Token::Delete)
|
if (_operator == Token::Delete)
|
||||||
return make_shared<TupleType>();
|
return TypeResult{make_shared<TupleType>()};
|
||||||
// we allow +, -, ++ and --
|
// we allow -, ++ and --
|
||||||
else if (_operator == Token::Add || _operator == Token::Sub ||
|
else if (_operator == Token::Sub || _operator == Token::Inc ||
|
||||||
_operator == Token::Inc || _operator == Token::Dec ||
|
_operator == Token::Dec || _operator == Token::BitNot)
|
||||||
_operator == Token::BitNot)
|
return TypeResult{shared_from_this()};
|
||||||
return shared_from_this();
|
|
||||||
else
|
else
|
||||||
return TypePointer();
|
return TypeResult{""};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntegerType::operator==(Type const& _other) const
|
bool IntegerType::operator==(Type const& _other) const
|
||||||
@ -651,7 +665,7 @@ bigint IntegerType::maxValue() const
|
|||||||
return (bigint(1) << m_bits) - 1;
|
return (bigint(1) << m_bits) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
_other->category() != Category::RationalNumber &&
|
_other->category() != Category::RationalNumber &&
|
||||||
@ -679,9 +693,8 @@ TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
|
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
|
||||||
{
|
{
|
||||||
// Signed EXP is not allowed
|
|
||||||
if (Token::Exp == _operator && intType->isSigned())
|
if (Token::Exp == _operator && intType->isSigned())
|
||||||
return TypePointer();
|
return TypeResult{"Exponentiation is not allowed for signed integer types."};
|
||||||
}
|
}
|
||||||
else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
|
else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
|
||||||
if (Token::Exp == _operator)
|
if (Token::Exp == _operator)
|
||||||
@ -704,7 +717,7 @@ string FixedPointType::richIdentifier() const
|
|||||||
return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits);
|
return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() == category())
|
if (_convertTo.category() == category())
|
||||||
{
|
{
|
||||||
@ -717,18 +730,18 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
|
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
|
TypeResult FixedPointType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
switch(_operator)
|
switch(_operator)
|
||||||
{
|
{
|
||||||
case Token::Delete:
|
case Token::Delete:
|
||||||
// "delete" is ok for all fixed types
|
// "delete" is ok for all fixed types
|
||||||
return make_shared<TupleType>();
|
return TypeResult(make_shared<TupleType>());
|
||||||
case Token::Add:
|
case Token::Add:
|
||||||
case Token::Sub:
|
case Token::Sub:
|
||||||
case Token::Inc:
|
case Token::Inc:
|
||||||
@ -771,7 +784,7 @@ bigint FixedPointType::minIntegerValue() const
|
|||||||
return bigint(0);
|
return bigint(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
auto commonType = Type::commonType(shared_from_this(), _other);
|
auto commonType = Type::commonType(shared_from_this(), _other);
|
||||||
|
|
||||||
@ -957,7 +970,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
|||||||
return make_tuple(true, value);
|
return make_tuple(true, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
switch (_convertTo.category())
|
switch (_convertTo.category())
|
||||||
{
|
{
|
||||||
@ -966,27 +979,21 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
if (isFractional())
|
if (isFractional())
|
||||||
return false;
|
return false;
|
||||||
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
|
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
|
||||||
if (m_value == rational(0))
|
return fitsIntegerType(m_value.numerator(), targetType);
|
||||||
return true;
|
|
||||||
unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
|
|
||||||
if (m_value > rational(0))
|
|
||||||
{
|
|
||||||
if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit)))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (targetType.isSigned())
|
|
||||||
{
|
|
||||||
if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
case Category::FixedPoint:
|
case Category::FixedPoint:
|
||||||
{
|
{
|
||||||
if (auto fixed = fixedPointType())
|
FixedPointType const& targetType = dynamic_cast<FixedPointType const&>(_convertTo);
|
||||||
return fixed->isImplicitlyConvertibleTo(_convertTo);
|
// Store a negative number into an unsigned.
|
||||||
|
if (isNegative() && !targetType.isSigned())
|
||||||
return false;
|
return false;
|
||||||
|
if (!isFractional())
|
||||||
|
return (targetType.minIntegerValue() <= m_value) && (m_value <= targetType.maxIntegerValue());
|
||||||
|
rational value = m_value * pow(bigint(10), targetType.fractionalDigits());
|
||||||
|
// Need explicit conversion since truncation will occur.
|
||||||
|
if (value.denominator() != 1)
|
||||||
|
return false;
|
||||||
|
return fitsIntoBits(value.numerator(), targetType.numBits(), targetType.isSigned());
|
||||||
}
|
}
|
||||||
case Category::FixedBytes:
|
case Category::FixedBytes:
|
||||||
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
|
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
|
||||||
@ -995,7 +1002,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (isImplicitlyConvertibleTo(_convertTo))
|
if (isImplicitlyConvertibleTo(_convertTo))
|
||||||
return true;
|
return true;
|
||||||
@ -1008,7 +1015,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
|
TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
rational value;
|
rational value;
|
||||||
switch (_operator)
|
switch (_operator)
|
||||||
@ -1029,10 +1036,10 @@ TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
|
|||||||
default:
|
default:
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
return make_shared<RationalNumberType>(value);
|
return TypeResult(make_shared<RationalNumberType>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
|
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
|
||||||
{
|
{
|
||||||
@ -1129,9 +1136,8 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
|
|||||||
|
|
||||||
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
|
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
|
||||||
|
|
||||||
// Limit size to 4096 bits
|
|
||||||
if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp))
|
if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp))
|
||||||
return TypePointer();
|
return TypeResult{"Precision of rational constants is limited to 4096 bits."};
|
||||||
|
|
||||||
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
|
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
|
||||||
if (_base == 1)
|
if (_base == 1)
|
||||||
@ -1212,9 +1218,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
|
|||||||
|
|
||||||
// verify that numerator and denominator fit into 4096 bit after every operation
|
// verify that numerator and denominator fit into 4096 bit after every operation
|
||||||
if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096)
|
if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096)
|
||||||
return TypePointer();
|
return TypeResult{"Precision of rational constants is limited to 4096 bits."};
|
||||||
|
|
||||||
return make_shared<RationalNumberType>(value);
|
return TypeResult(make_shared<RationalNumberType>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1354,7 +1360,7 @@ StringLiteralType::StringLiteralType(Literal const& _literal):
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
|
if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
|
||||||
return size_t(fixedBytes->numBytes()) >= m_value.size();
|
return size_t(fixedBytes->numBytes()) >= m_value.size();
|
||||||
@ -1409,7 +1415,7 @@ FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() != category())
|
if (_convertTo.category() != category())
|
||||||
return false;
|
return false;
|
||||||
@ -1417,7 +1423,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return convertTo.m_bytes >= m_bytes;
|
return convertTo.m_bytes >= m_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
|
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
|
||||||
(_convertTo.category() == Category::Address && numBytes() == 20) ||
|
(_convertTo.category() == Category::Address && numBytes() == 20) ||
|
||||||
@ -1425,18 +1431,18 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
_convertTo.category() == category();
|
_convertTo.category() == category();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
|
TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
// "delete" and "~" is okay for FixedBytesType
|
// "delete" and "~" is okay for FixedBytesType
|
||||||
if (_operator == Token::Delete)
|
if (_operator == Token::Delete)
|
||||||
return make_shared<TupleType>();
|
return TypeResult(make_shared<TupleType>());
|
||||||
else if (_operator == Token::BitNot)
|
else if (_operator == Token::BitNot)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
|
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
if (TokenTraits::isShiftOp(_operator))
|
if (TokenTraits::isShiftOp(_operator))
|
||||||
{
|
{
|
||||||
@ -1452,7 +1458,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer co
|
|||||||
|
|
||||||
// FixedBytes can be compared and have bitwise operators applied to them
|
// FixedBytes can be compared and have bitwise operators applied to them
|
||||||
if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
|
if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
|
||||||
return commonType;
|
return TypeResult(commonType);
|
||||||
|
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
@ -1486,14 +1492,14 @@ u256 BoolType::literalValue(Literal const* _literal) const
|
|||||||
solAssert(false, "Bool type constructed from non-boolean literal.");
|
solAssert(false, "Bool type constructed from non-boolean literal.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer BoolType::unaryOperatorResult(Token _operator) const
|
TypeResult BoolType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
if (_operator == Token::Delete)
|
if (_operator == Token::Delete)
|
||||||
return make_shared<TupleType>();
|
return TypeResult(make_shared<TupleType>());
|
||||||
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
|
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
if (category() != _other->category())
|
if (category() != _other->category())
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
@ -1503,7 +1509,7 @@ TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (*this == _convertTo)
|
if (*this == _convertTo)
|
||||||
return true;
|
return true;
|
||||||
@ -1520,7 +1526,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
|
if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
|
||||||
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
|
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
|
||||||
@ -1533,14 +1539,14 @@ bool ContractType::isPayable() const
|
|||||||
return fallbackFunction && fallbackFunction->isPayable();
|
return fallbackFunction && fallbackFunction->isPayable();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer ContractType::unaryOperatorResult(Token _operator) const
|
TypeResult ContractType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
if (isSuper())
|
if (isSuper())
|
||||||
return TypePointer{};
|
return TypePointer{};
|
||||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
|
TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
if (_operator != Token::Delete)
|
if (_operator != Token::Delete)
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
@ -1551,7 +1557,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
|
|||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
return make_shared<TupleType>();
|
return TypeResult(make_shared<TupleType>());
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
return m_isPointer ? TypePointer() : make_shared<TupleType>();
|
return m_isPointer ? TypePointer() : make_shared<TupleType>();
|
||||||
}
|
}
|
||||||
@ -1605,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() != category())
|
if (_convertTo.category() != category())
|
||||||
return false;
|
return false;
|
||||||
@ -1645,7 +1651,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
|
BoolResult ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
|
||||||
{
|
{
|
||||||
if (isImplicitlyConvertibleTo(_convertTo))
|
if (isImplicitlyConvertibleTo(_convertTo))
|
||||||
return true;
|
return true;
|
||||||
@ -1815,23 +1821,23 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
|||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
if (!isString())
|
if (!isString())
|
||||||
{
|
{
|
||||||
members.push_back({"length", make_shared<IntegerType>(256)});
|
members.emplace_back("length", make_shared<IntegerType>(256));
|
||||||
if (isDynamicallySized() && location() == DataLocation::Storage)
|
if (isDynamicallySized() && location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
members.push_back({"push", make_shared<FunctionType>(
|
members.emplace_back("push", make_shared<FunctionType>(
|
||||||
TypePointers{baseType()},
|
TypePointers{baseType()},
|
||||||
TypePointers{make_shared<IntegerType>(256)},
|
TypePointers{make_shared<IntegerType>(256)},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
|
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
|
||||||
)});
|
));
|
||||||
members.push_back({"pop", make_shared<FunctionType>(
|
members.emplace_back("pop", make_shared<FunctionType>(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
strings{string()},
|
strings{string()},
|
||||||
FunctionType::Kind::ArrayPop
|
FunctionType::Kind::ArrayPop
|
||||||
)});
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
@ -1960,21 +1966,17 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!functionWithEqualArgumentsFound)
|
if (!functionWithEqualArgumentsFound)
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(function->name(), functionType, function);
|
||||||
function->name(),
|
|
||||||
functionType,
|
|
||||||
function
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!m_contract.isLibrary())
|
else if (!m_contract.isLibrary())
|
||||||
{
|
{
|
||||||
for (auto const& it: m_contract.interfaceFunctions())
|
for (auto const& it: m_contract.interfaceFunctions())
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(
|
||||||
it.second->declaration().name(),
|
it.second->declaration().name(),
|
||||||
it.second->asCallableFunction(m_contract.isLibrary()),
|
it.second->asCallableFunction(m_contract.isLibrary()),
|
||||||
&it.second->declaration()
|
&it.second->declaration()
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
@ -2002,11 +2004,11 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
|
|||||||
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
|
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
|
||||||
for (size_t index = 0; index < variables.size(); ++index)
|
for (size_t index = 0; index < variables.size(); ++index)
|
||||||
if (auto const* offset = offsets.offset(index))
|
if (auto const* offset = offsets.offset(index))
|
||||||
variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
|
variablesAndOffsets.emplace_back(variables[index], offset->first, offset->second);
|
||||||
return variablesAndOffsets;
|
return variablesAndOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() != category())
|
if (_convertTo.category() != category())
|
||||||
return false;
|
return false;
|
||||||
@ -2092,10 +2094,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
|||||||
// Skip all mapping members if we are not in storage.
|
// Skip all mapping members if we are not in storage.
|
||||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
||||||
continue;
|
continue;
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(
|
||||||
variable->name(),
|
variable->name(),
|
||||||
copyForLocationIfReference(type),
|
copyForLocationIfReference(type),
|
||||||
variable.get())
|
variable.get()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
@ -2249,7 +2251,7 @@ bool StructType::recursive() const
|
|||||||
return *m_recursive;
|
return *m_recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer EnumType::unaryOperatorResult(Token _operator) const
|
TypeResult EnumType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||||
}
|
}
|
||||||
@ -2291,7 +2293,7 @@ size_t EnumType::numberOfMembers() const
|
|||||||
return m_enum.members().size();
|
return m_enum.members().size();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return _convertTo == *this || _convertTo.category() == Category::Integer;
|
return _convertTo == *this || _convertTo.category() == Category::Integer;
|
||||||
}
|
}
|
||||||
@ -2308,7 +2310,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const
|
|||||||
solAssert(false, "Requested unknown enum value " + _member);
|
solAssert(false, "Requested unknown enum value " + _member);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
|
BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
|
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
|
||||||
{
|
{
|
||||||
@ -2432,7 +2434,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
m_parameterTypes.push_back(mappingType->keyType());
|
m_parameterTypes.push_back(mappingType->keyType());
|
||||||
m_parameterNames.push_back("");
|
m_parameterNames.emplace_back("");
|
||||||
returnType = mappingType->valueType();
|
returnType = mappingType->valueType();
|
||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
||||||
@ -2441,7 +2443,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
// Return byte arrays as whole.
|
// Return byte arrays as whole.
|
||||||
break;
|
break;
|
||||||
returnType = arrayType->baseType();
|
returnType = arrayType->baseType();
|
||||||
m_parameterNames.push_back("");
|
m_parameterNames.emplace_back("");
|
||||||
m_parameterTypes.push_back(make_shared<IntegerType>(256));
|
m_parameterTypes.push_back(make_shared<IntegerType>(256));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2472,7 +2474,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
DataLocation::Memory,
|
DataLocation::Memory,
|
||||||
returnType
|
returnType
|
||||||
));
|
));
|
||||||
m_returnParameterNames.push_back("");
|
m_returnParameterNames.emplace_back("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2648,14 +2650,14 @@ bool FunctionType::operator==(Type const& _other) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (m_kind == Kind::External && _convertTo == AddressType::address())
|
if (m_kind == Kind::External && _convertTo == AddressType::address())
|
||||||
return true;
|
return true;
|
||||||
return _convertTo.category() == category();
|
return _convertTo.category() == category();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.category() != category())
|
if (_convertTo.category() != category())
|
||||||
return false;
|
return false;
|
||||||
@ -2680,14 +2682,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FunctionType::unaryOperatorResult(Token _operator) const
|
TypeResult FunctionType::unaryOperatorResult(Token _operator) const
|
||||||
{
|
{
|
||||||
if (_operator == Token::Delete)
|
if (_operator == Token::Delete)
|
||||||
return make_shared<TupleType>();
|
return TypeResult(make_shared<TupleType>());
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
TypeResult FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
|
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
@ -2841,14 +2843,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
{
|
{
|
||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
if (m_kind == Kind::External)
|
if (m_kind == Kind::External)
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back("selector", make_shared<FixedBytesType>(4));
|
||||||
"selector",
|
|
||||||
make_shared<FixedBytesType>(4)
|
|
||||||
));
|
|
||||||
if (m_kind != Kind::BareDelegateCall)
|
if (m_kind != Kind::BareDelegateCall)
|
||||||
{
|
{
|
||||||
if (isPayable())
|
if (isPayable())
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(
|
||||||
"value",
|
"value",
|
||||||
make_shared<FunctionType>(
|
make_shared<FunctionType>(
|
||||||
parseElementaryTypeVector({"uint"}),
|
parseElementaryTypeVector({"uint"}),
|
||||||
@ -2862,10 +2861,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
m_gasSet,
|
m_gasSet,
|
||||||
m_valueSet
|
m_valueSet
|
||||||
)
|
)
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
if (m_kind != Kind::Creation)
|
if (m_kind != Kind::Creation)
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(
|
||||||
"gas",
|
"gas",
|
||||||
make_shared<FunctionType>(
|
make_shared<FunctionType>(
|
||||||
parseElementaryTypeVector({"uint"}),
|
parseElementaryTypeVector({"uint"}),
|
||||||
@ -2879,7 +2878,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
m_gasSet,
|
m_gasSet,
|
||||||
m_valueSet
|
m_valueSet
|
||||||
)
|
)
|
||||||
));
|
);
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -3207,24 +3206,24 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
|||||||
if (contract.isLibrary())
|
if (contract.isLibrary())
|
||||||
for (FunctionDefinition const* function: contract.definedFunctions())
|
for (FunctionDefinition const* function: contract.definedFunctions())
|
||||||
if (function->isVisibleAsLibraryMember())
|
if (function->isVisibleAsLibraryMember())
|
||||||
members.push_back(MemberList::Member(
|
members.emplace_back(
|
||||||
function->name(),
|
function->name(),
|
||||||
FunctionType(*function).asCallableFunction(true),
|
FunctionType(*function).asCallableFunction(true),
|
||||||
function
|
function
|
||||||
));
|
);
|
||||||
if (isBase)
|
if (isBase)
|
||||||
{
|
{
|
||||||
// We are accessing the type of a base contract, so add all public and protected
|
// We are accessing the type of a base contract, so add all public and protected
|
||||||
// members. Note that this does not add inherited functions on purpose.
|
// members. Note that this does not add inherited functions on purpose.
|
||||||
for (Declaration const* decl: contract.inheritableMembers())
|
for (Declaration const* decl: contract.inheritableMembers())
|
||||||
members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
|
members.emplace_back(decl->name(), decl->type(), decl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto const& stru: contract.definedStructs())
|
for (auto const& stru: contract.definedStructs())
|
||||||
members.push_back(MemberList::Member(stru->name(), stru->type(), stru));
|
members.emplace_back(stru->name(), stru->type(), stru);
|
||||||
for (auto const& enu: contract.definedEnums())
|
for (auto const& enu: contract.definedEnums())
|
||||||
members.push_back(MemberList::Member(enu->name(), enu->type(), enu));
|
members.emplace_back(enu->name(), enu->type(), enu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_actualType->category() == Category::Enum)
|
else if (m_actualType->category() == Category::Enum)
|
||||||
@ -3232,7 +3231,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
|||||||
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
|
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
|
||||||
auto enumType = make_shared<EnumType>(enumDef);
|
auto enumType = make_shared<EnumType>(enumDef);
|
||||||
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
|
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
|
||||||
members.push_back(MemberList::Member(enumValue->name(), enumType));
|
members.emplace_back(enumValue->name(), enumType);
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
@ -3297,7 +3296,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
|
|||||||
MemberList::MemberMap symbols;
|
MemberList::MemberMap symbols;
|
||||||
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
|
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
|
||||||
for (Declaration const* symbol: symbolName.second)
|
for (Declaration const* symbol: symbolName.second)
|
||||||
symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
|
symbols.emplace_back(symbolName.first, symbol->type(), symbol);
|
||||||
return symbols;
|
return symbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,22 +22,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
|
||||||
#include <libsolidity/ast/ASTEnums.h>
|
#include <libsolidity/ast/ASTEnums.h>
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
#include <libsolidity/parsing/Token.h>
|
#include <libsolidity/parsing/Token.h>
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
|
#include <libdevcore/Result.h>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/rational.hpp>
|
#include <boost/rational.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -50,6 +51,8 @@ using TypePointer = std::shared_ptr<Type const>;
|
|||||||
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
||||||
using TypePointers = std::vector<TypePointer>;
|
using TypePointers = std::vector<TypePointer>;
|
||||||
using rational = boost::rational<dev::bigint>;
|
using rational = boost::rational<dev::bigint>;
|
||||||
|
using TypeResult = Result<TypePointer>;
|
||||||
|
using BoolResult = Result<bool>;
|
||||||
|
|
||||||
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
|
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
|
||||||
{
|
{
|
||||||
@ -63,6 +66,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
|
|||||||
|
|
||||||
enum class DataLocation { Storage, CallData, Memory };
|
enum class DataLocation { Storage, CallData, Memory };
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to compute storage offsets of members of structs and contracts.
|
* Helper class to compute storage offsets of members of structs and contracts.
|
||||||
*/
|
*/
|
||||||
@ -189,19 +193,19 @@ public:
|
|||||||
/// @returns an escaped identifier (will not contain any parenthesis or commas)
|
/// @returns an escaped identifier (will not contain any parenthesis or commas)
|
||||||
static std::string escapeIdentifier(std::string const& _identifier);
|
static std::string escapeIdentifier(std::string const& _identifier);
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
virtual BoolResult isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
virtual BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return isImplicitlyConvertibleTo(_convertTo);
|
return isImplicitlyConvertibleTo(_convertTo);
|
||||||
}
|
}
|
||||||
/// @returns the resulting type of applying the given unary operator or an empty pointer if
|
/// @returns the resulting type of applying the given unary operator or an empty pointer if
|
||||||
/// this is not possible.
|
/// this is not possible.
|
||||||
/// The default implementation does not allow any unary operator.
|
/// The default implementation does not allow any unary operator.
|
||||||
virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); }
|
virtual TypeResult unaryOperatorResult(Token) const { return TypePointer(); }
|
||||||
/// @returns the resulting type of applying the given binary operator or an empty pointer if
|
/// @returns the resulting type of applying the given binary operator or an empty pointer if
|
||||||
/// this is not possible.
|
/// this is not possible.
|
||||||
/// The default implementation allows comparison operators if a common type exists
|
/// The default implementation allows comparison operators if a common type exists
|
||||||
virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
virtual TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||||
{
|
{
|
||||||
return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
|
return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
|
||||||
}
|
}
|
||||||
@ -336,10 +340,10 @@ public:
|
|||||||
explicit AddressType(StateMutability _stateMutability);
|
explicit AddressType(StateMutability _stateMutability);
|
||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
@ -381,10 +385,10 @@ public:
|
|||||||
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
|
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
|
||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
@ -423,10 +427,10 @@ public:
|
|||||||
explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
|
explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
|
||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
@ -476,11 +480,10 @@ public:
|
|||||||
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
|
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
|
||||||
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
|
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
|
||||||
{}
|
{}
|
||||||
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
|
||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
@ -536,8 +539,8 @@ public:
|
|||||||
|
|
||||||
explicit StringLiteralType(Literal const& _literal);
|
explicit StringLiteralType(Literal const& _literal);
|
||||||
|
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||||
{
|
{
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
@ -570,12 +573,12 @@ public:
|
|||||||
|
|
||||||
explicit FixedBytesType(unsigned _bytes);
|
explicit FixedBytesType(unsigned _bytes);
|
||||||
|
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
|
unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
|
||||||
unsigned storageBytes() const override { return m_bytes; }
|
unsigned storageBytes() const override { return m_bytes; }
|
||||||
@ -598,11 +601,10 @@ private:
|
|||||||
class BoolType: public Type
|
class BoolType: public Type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BoolType() {}
|
|
||||||
Category category() const override { return Category::Bool; }
|
Category category() const override { return Category::Bool; }
|
||||||
std::string richIdentifier() const override { return "t_bool"; }
|
std::string richIdentifier() const override { return "t_bool"; }
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
|
unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
|
||||||
unsigned storageBytes() const override { return 1; }
|
unsigned storageBytes() const override { return 1; }
|
||||||
@ -624,8 +626,8 @@ public:
|
|||||||
explicit ReferenceType(DataLocation _location): m_location(_location) {}
|
explicit ReferenceType(DataLocation _location): m_location(_location) {}
|
||||||
DataLocation location() const { return m_location; }
|
DataLocation location() const { return m_location; }
|
||||||
|
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||||
{
|
{
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
@ -702,8 +704,8 @@ public:
|
|||||||
m_length(_length)
|
m_length(_length)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(const Type& _other) const override;
|
bool operator==(const Type& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override;
|
unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
@ -757,10 +759,10 @@ public:
|
|||||||
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
|
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
|
||||||
m_contract(_contract), m_super(_super) {}
|
m_contract(_contract), m_super(_super) {}
|
||||||
/// Contracts can be implicitly converted only to base contracts.
|
/// Contracts can be implicitly converted only to base contracts.
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
/// Contracts can only be explicitly converted to address types and base contracts.
|
/// Contracts can only be explicitly converted to address types and base contracts.
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded ) const override
|
unsigned calldataEncodedSize(bool _padded ) const override
|
||||||
@ -821,7 +823,7 @@ public:
|
|||||||
Category category() const override { return Category::Struct; }
|
Category category() const override { return Category::Struct; }
|
||||||
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
|
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
|
||||||
ReferenceType(_location), m_struct(_struct) {}
|
ReferenceType(_location), m_struct(_struct) {}
|
||||||
bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override;
|
unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
@ -876,7 +878,7 @@ class EnumType: public Type
|
|||||||
public:
|
public:
|
||||||
Category category() const override { return Category::Enum; }
|
Category category() const override { return Category::Enum; }
|
||||||
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
|
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override
|
unsigned calldataEncodedSize(bool _padded) const override
|
||||||
@ -889,7 +891,7 @@ public:
|
|||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
bool isValueType() const override { return true; }
|
bool isValueType() const override { return true; }
|
||||||
|
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer encodingType() const override
|
TypePointer encodingType() const override
|
||||||
{
|
{
|
||||||
return std::make_shared<IntegerType>(8 * int(storageBytes()));
|
return std::make_shared<IntegerType>(8 * int(storageBytes()));
|
||||||
@ -917,10 +919,10 @@ class TupleType: public Type
|
|||||||
public:
|
public:
|
||||||
Category category() const override { return Category::Tuple; }
|
Category category() const override { return Category::Tuple; }
|
||||||
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
|
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
|
||||||
bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
std::string toString(bool) const override;
|
std::string toString(bool) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
@ -1065,10 +1067,10 @@ public:
|
|||||||
|
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override;
|
||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
unsigned calldataEncodedSize(bool _padded) const override;
|
unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
@ -1197,7 +1199,7 @@ public:
|
|||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
bool canLiveOutsideStorage() const override { return false; }
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
TypePointer encodingType() const override
|
TypePointer encodingType() const override
|
||||||
{
|
{
|
||||||
return std::make_shared<IntegerType>(256);
|
return std::make_shared<IntegerType>(256);
|
||||||
@ -1230,7 +1232,7 @@ public:
|
|||||||
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
|
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
|
||||||
TypePointer const& actualType() const { return m_actualType; }
|
TypePointer const& actualType() const { return m_actualType; }
|
||||||
|
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
@ -1255,7 +1257,7 @@ public:
|
|||||||
Category category() const override { return Category::Modifier; }
|
Category category() const override { return Category::Modifier; }
|
||||||
explicit ModifierType(ModifierDefinition const& _modifier);
|
explicit ModifierType(ModifierDefinition const& _modifier);
|
||||||
|
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
bool canLiveOutsideStorage() const override { return false; }
|
||||||
@ -1281,7 +1283,7 @@ public:
|
|||||||
|
|
||||||
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
|
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
|
||||||
|
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
@ -1308,7 +1310,7 @@ public:
|
|||||||
|
|
||||||
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
||||||
|
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||||
{
|
{
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
@ -1339,9 +1341,9 @@ public:
|
|||||||
Category category() const override { return Category::InaccessibleDynamic; }
|
Category category() const override { return Category::InaccessibleDynamic; }
|
||||||
|
|
||||||
std::string richIdentifier() const override { return "t_inaccessible"; }
|
std::string richIdentifier() const override { return "t_inaccessible"; }
|
||||||
bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
BoolResult isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||||
bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
BoolResult isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||||
unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
|
unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
bool canLiveOutsideStorage() const override { return false; }
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
|
|
||||||
#include <libdevcore/Whiskers.h>
|
#include <libdevcore/Whiskers.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
@ -141,8 +140,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
|||||||
vector<string> valueNamesLocal;
|
vector<string> valueNamesLocal;
|
||||||
for (size_t j = 0; j < sizeOnStack; j++)
|
for (size_t j = 0; j < sizeOnStack; j++)
|
||||||
{
|
{
|
||||||
valueNamesLocal.push_back("value" + to_string(stackPos));
|
valueNamesLocal.emplace_back("value" + to_string(stackPos));
|
||||||
valueReturnParams.push_back("value" + to_string(stackPos));
|
valueReturnParams.emplace_back("value" + to_string(stackPos));
|
||||||
stackPos++;
|
stackPos++;
|
||||||
}
|
}
|
||||||
bool dynamic = decodingTypes[i]->isDynamicallyEncoded();
|
bool dynamic = decodingTypes[i]->isDynamicallyEncoded();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user