Merge pull request #5697 from ethereum/develop

Merge develop into release for 0.5.2
This commit is contained in:
chriseth 2018-12-19 18:06:13 +01:00 committed by GitHub
commit 1df8f40cd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
512 changed files with 7675 additions and 2708 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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.

View File

@ -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)

View File

@ -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.

View File

@ -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"
} }
} }

View File

@ -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 }

View File

@ -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
-------------------------------- --------------------------------

View File

@ -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?
==================== ====================

View File

@ -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
============= =============

View File

@ -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

View File

@ -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

View File

@ -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,

View 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
-------------------------- --------------------------

View File

@ -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 |
+-------------+-------------------------------------------+-------------------------------+

View File

@ -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``.

View File

@ -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: {},

View File

@ -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)

View File

@ -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')

View File

@ -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();
} }

View File

@ -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); }

View File

@ -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()

View File

@ -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
View 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;
};
}

View File

@ -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 ");
} }

View File

@ -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;
}
} }

View File

@ -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;

View File

@ -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.

View File

@ -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());

View File

@ -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;

View File

@ -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
{ {

View File

@ -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})

View File

@ -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;

View File

@ -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++;

View File

@ -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(

View File

@ -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())
) )

View File

@ -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)

View File

@ -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

View File

@ -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})

View File

@ -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};
}; };
} }

View File

@ -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}; }

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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;
}; };

View 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,
};
}

View 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 = "");
}
}

View File

@ -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);
} }
} }

View File

@ -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;
}; };
} }

View File

@ -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})

View File

@ -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); };

View File

@ -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);

View File

@ -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();
} }

View File

@ -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()

View File

@ -20,6 +20,8 @@
* Public compiler API. * Public compiler API.
*/ */
#pragma once
#include <stdbool.h> #include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -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()

View File

@ -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(),

View File

@ -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)
); );
} }
} }

View File

@ -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

View File

@ -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."
); );
} }
} }

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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];
}

View File

@ -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;
}; };
} }

View File

@ -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>

View File

@ -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
{ {

View File

@ -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;

View File

@ -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;

View File

@ -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
{ {

View File

@ -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;

View File

@ -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
{ {

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;

View File

@ -21,6 +21,7 @@
*/ */
#include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/analysis/SemVerHandler.h>
#include <functional> #include <functional>
using namespace std; using namespace std;

View File

@ -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)

View File

@ -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(),

View File

@ -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;

View File

@ -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
{ {

View File

@ -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

View File

@ -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);
} }
} }
} }

View File

@ -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
{ {

View File

@ -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;
}; };

View File

@ -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

View File

@ -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 ?

View File

@ -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
{ {

View File

@ -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;

View File

@ -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
{ {

View File

@ -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
{ {

View File

@ -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;
} }

View File

@ -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; }

View File

@ -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