mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5697 from ethereum/develop
Merge develop into release for 0.5.2
This commit is contained in:
commit
1df8f40cd2
@ -15,7 +15,7 @@ defaults:
|
||||
mkdir -p build
|
||||
cd build
|
||||
[ -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
|
||||
- run_tests: &run_tests
|
||||
name: Tests
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
- version.txt
|
||||
test_emscripten_solcjs:
|
||||
docker:
|
||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||
- image: circleci/node:10
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
@ -78,23 +78,15 @@ jobs:
|
||||
- run:
|
||||
name: Install external tests deps
|
||||
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
|
||||
npm --version
|
||||
- run:
|
||||
name: Test solcjs
|
||||
command: |
|
||||
. /usr/local/nvm/nvm.sh
|
||||
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
|
||||
test_emscripten_external:
|
||||
docker:
|
||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||
- image: circleci/node:10
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
@ -104,19 +96,11 @@ jobs:
|
||||
- run:
|
||||
name: Install external tests deps
|
||||
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
|
||||
npm --version
|
||||
- run:
|
||||
name: External tests
|
||||
command: |
|
||||
. /usr/local/nvm/nvm.sh
|
||||
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
|
||||
build_x86_linux:
|
||||
docker:
|
||||
|
39
.travis.yml
39
.travis.yml
@ -55,6 +55,18 @@ matrix:
|
||||
env:
|
||||
- ZIP_SUFFIX=ubuntu-trusty
|
||||
- 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
|
||||
dist: trusty
|
||||
@ -63,6 +75,18 @@ matrix:
|
||||
env:
|
||||
- ZIP_SUFFIX=ubuntu-trusty-clang
|
||||
- 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
|
||||
- os: linux
|
||||
@ -159,18 +183,15 @@ cache:
|
||||
install:
|
||||
- test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.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:
|
||||
# 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_DOCKER != On || (scripts/docker_build.sh)
|
||||
- test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE
|
||||
&& scripts/release.sh $ZIP_SUFFIX
|
||||
&& scripts/create_source_tarball.sh)
|
||||
- test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE -DBoost_USE_STATIC_LIBS=OFF && scripts/create_source_tarball.sh)
|
||||
|
||||
script:
|
||||
- test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh)
|
||||
@ -204,7 +225,7 @@ deploy:
|
||||
- release
|
||||
- /^v\d/
|
||||
# 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.
|
||||
- provider: releases
|
||||
|
@ -8,7 +8,7 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# 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})
|
||||
|
||||
option(LLL "Build LLL" OFF)
|
||||
|
29
Changelog.md
29
Changelog.md
@ -1,3 +1,32 @@
|
||||
### 0.5.2 (2018-12-19)
|
||||
|
||||
Language Features:
|
||||
* Control Flow Graph: Detect every access to uninitialized storage pointers.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Inline Assembly: Improve error messages around invalid function argument count.
|
||||
* Code Generator: Only check callvalue once if all functions are non-payable.
|
||||
* Code Generator: Use codecopy for string constants more aggressively.
|
||||
* Code Generator: Use binary search for dispatch function if more efficient. The size/speed tradeoff can be tuned using ``--optimize-runs``.
|
||||
* SMTChecker: Support mathematical and cryptographic functions in an uninterpreted way.
|
||||
* SMTChecker: Support one-dimensional mappings.
|
||||
* Standard JSON Interface: Disallow unknown keys in standard JSON input.
|
||||
* Standard JSON Interface: Only run code generation if it has been requested. This could lead to unsupported feature errors only being reported at the point where you request bytecode.
|
||||
* Static Analyzer: Do not warn about unused variables or state mutability for functions with an empty body.
|
||||
* Type Checker: Add an additional reason to be displayed when type conversion fails.
|
||||
* Yul: Support object access via ``datasize``, ``dataoffset`` and ``datacopy`` in standalone assembly mode.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Standard JSON Interface: Report specific error message for json input errors instead of internal compiler error.
|
||||
|
||||
|
||||
Build System:
|
||||
* Replace the trusty PPA build by a static build on cosmic that is used for the trusty package instead.
|
||||
* Remove support for Visual Studio 2015.
|
||||
|
||||
|
||||
### 0.5.1 (2018-12-03)
|
||||
|
||||
Language Features:
|
||||
|
@ -10,6 +10,7 @@ Checklist for making a release:
|
||||
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
|
||||
- [ ] 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).
|
||||
- [ ] 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``).
|
||||
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
|
||||
- [ ] Update the default version on readthedocs.
|
||||
|
@ -32,10 +32,9 @@ branches:
|
||||
- release
|
||||
- develop
|
||||
configuration:
|
||||
- RelWithDebInfo
|
||||
- Release
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
# This is used for pushing to solidity-test-bytecodes
|
||||
priv_key:
|
||||
@ -63,12 +62,11 @@ install:
|
||||
before_build:
|
||||
- if not exist build mkdir build
|
||||
- cd build
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On )
|
||||
else ( cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On )
|
||||
- cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On
|
||||
build_script:
|
||||
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
|
||||
- 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
|
||||
|
||||
test_script:
|
||||
|
@ -25,8 +25,8 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
||||
|
||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||
|
||||
# Use ISO C++11 standard language.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
# Use ISO C++14 standard language.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
|
||||
# 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
|
||||
|
@ -1,3 +1,8 @@
|
||||
find_library(CLN_LIBRARY NAMES cln)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY)
|
||||
|
||||
if(CLN_FOUND AND NOT TARGET CLN::CLN)
|
||||
add_library(CLN::CLN UNKNOWN IMPORTED)
|
||||
set_property(TARGET CLN::CLN PROPERTY IMPORTED_LOCATION ${CLN_LIBRARY})
|
||||
endif()
|
||||
|
@ -14,12 +14,19 @@ if (USE_CVC4)
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARY})
|
||||
|
||||
if (CLN_FOUND)
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${CLN_LIBRARY})
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} CLN::CLN)
|
||||
endif ()
|
||||
|
||||
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()
|
||||
else()
|
||||
set(CVC4_FOUND FALSE)
|
||||
|
@ -1,3 +1,8 @@
|
||||
find_library(GMP_LIBRARY NAMES gmp )
|
||||
find_library(GMP_LIBRARY NAMES gmp)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
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()
|
||||
|
@ -1,9 +1,14 @@
|
||||
if (USE_Z3)
|
||||
find_path(Z3_INCLUDE_DIR NAMES z3++.h PATH_SUFFIXES z3)
|
||||
find_library(Z3_LIBRARY NAMES z3 )
|
||||
find_library(Z3_LIBRARY NAMES z3)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
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()
|
||||
set(Z3_FOUND FALSE)
|
||||
endif()
|
||||
# TODO: Create IMPORTED library for Z3.
|
||||
|
@ -15,9 +15,9 @@ set(JSONCPP_INCLUDE_DIR "${prefix}/include")
|
||||
# versions used in the CI runs.
|
||||
if(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.
|
||||
set(JSONCPP_CXX_FLAGS -std=c++11)
|
||||
set(JSONCPP_CXX_FLAGS -std=c++14)
|
||||
else()
|
||||
set(JSONCPP_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
endif()
|
||||
@ -51,5 +51,6 @@ ExternalProject_Add(jsoncpp-project
|
||||
add_library(jsoncpp STATIC IMPORTED)
|
||||
file(MAKE_DIRECTORY ${JSONCPP_INCLUDE_DIR}) # Must exist.
|
||||
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})
|
||||
add_dependencies(jsoncpp jsoncpp-project)
|
||||
|
@ -159,8 +159,8 @@ following list can be used as a reference of its opcodes.
|
||||
|
||||
If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
|
||||
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
|
||||
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
|
||||
special and all others push exactly one item onto the stack.
|
||||
Opcodes marked with ``-`` do not push an item onto the stack (do not return a result),
|
||||
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.
|
||||
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
|
||||
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``.
|
||||
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
||||
|
||||
|
@ -616,5 +616,9 @@
|
||||
"0.5.1": {
|
||||
"bugs": [],
|
||||
"released": "2018-12-03"
|
||||
},
|
||||
"0.5.2": {
|
||||
"bugs": [],
|
||||
"released": "2018-12-19"
|
||||
}
|
||||
}
|
@ -1408,7 +1408,7 @@ Interfaces are denoted by their own keyword:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.11 <0.6.0;
|
||||
pragma solidity >=0.5.0 <0.6.0;
|
||||
|
||||
interface Token {
|
||||
enum TokenType { Fungible, NonFungible }
|
||||
|
@ -61,7 +61,7 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please
|
||||
follow the style of previous entries, when applicable.
|
||||
|
||||
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
|
||||
ensure that it builds locally before submitting a pull request.
|
||||
|
||||
@ -83,14 +83,14 @@ internally.
|
||||
|
||||
.. 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``.
|
||||
If you're running this in plain Command Prompt, use ``.\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\Release\soltest.exe -- --no-ipc --no-smt``.
|
||||
|
||||
The option ``--no-smt`` disables the tests that require ``libz3`` and
|
||||
``--no-ipc`` disables those that require ``aleth``.
|
||||
|
||||
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``.
|
||||
|
||||
@ -122,9 +122,9 @@ The CI runs additional tests (including ``solc-js`` and testing third party Soli
|
||||
|
||||
.. 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.
|
||||
Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``.
|
||||
Currently the CI uses version ``1.5.0-alpha.7`` of ``aleth``.
|
||||
|
||||
Writing and running syntax tests
|
||||
--------------------------------
|
||||
|
@ -9,35 +9,6 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_.
|
||||
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?
|
||||
===========================================================================================
|
||||
|
||||
@ -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
|
||||
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?
|
||||
====================
|
||||
|
||||
|
@ -296,13 +296,13 @@ And for Windows:
|
||||
This latter set of instructions should result in the creation of
|
||||
**solidity.sln** in that build directory. Double-clicking on that file
|
||||
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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --build . --config Release
|
||||
|
||||
CMake options
|
||||
=============
|
||||
|
@ -41,7 +41,7 @@ source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
|
||||
A contract in the sense of Solidity is a collection of code (its *functions*) and
|
||||
data (its *state*) that resides at a specific address on the Ethereum
|
||||
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
|
||||
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
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -224,9 +224,9 @@ for displaying the current position in the source code inside a debugger
|
||||
or for breakpoint handling.
|
||||
|
||||
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
|
||||
``"sourceList"``, which is part of the combined-json and the output of
|
||||
the json / npm compiler.
|
||||
The identifier of a source file is stored in
|
||||
``output['sources'][sourceName]['id']`` where ``output`` is the output of the
|
||||
standard-json compiler interface parsed as JSON.
|
||||
|
||||
.. note ::
|
||||
In the case of instructions that are not associated with any particular source file,
|
||||
|
@ -743,7 +743,7 @@ In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatu
|
||||
Extracting the Signature Parameters
|
||||
-----------------------------------
|
||||
|
||||
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
|
||||
--------------------------
|
||||
|
@ -1093,3 +1093,64 @@ General Recommendations
|
||||
=======================
|
||||
|
||||
TODO
|
||||
|
||||
.. _natspec:
|
||||
|
||||
*******
|
||||
NatSpec
|
||||
*******
|
||||
|
||||
Solidity contracts can have a form of comments that are the basis of the
|
||||
Ethereum Natural Language Specification Format.
|
||||
|
||||
Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation
|
||||
of one or multiple lines starting with `///` or a
|
||||
multiline comment starting with `/**` and ending with `*/`.
|
||||
|
||||
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
|
||||
added looks like the one below::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
/// @author The Solidity Team
|
||||
/// @title A simple storage example
|
||||
contract SimpleStorage {
|
||||
uint storedData;
|
||||
|
||||
/// Store `x`.
|
||||
/// @param x the new value to store
|
||||
/// @dev stores the number in the state variable `storedData`
|
||||
function set(uint x) public {
|
||||
storedData = x;
|
||||
}
|
||||
|
||||
/// Return the stored value.
|
||||
/// @dev retrieves the value of the state variable `storedData`
|
||||
/// @return the stored value
|
||||
function get() public view returns (uint) {
|
||||
return storedData;
|
||||
}
|
||||
}
|
||||
|
||||
Natspec uses doxygen style tags with some special meaning.
|
||||
If no tag is used, then the comment applies to ``@notice``.
|
||||
The ``@notice`` tag is the main NatSpec tag and its audience is
|
||||
users of the contract who have never seen the source code, so it should make
|
||||
as little assumptions about the inner details as possible.
|
||||
All tags are optional.
|
||||
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| Tag | Description | Context |
|
||||
+=============+===========================================+===============================+
|
||||
| ``@title`` | A title that describes the contract | contract, interface |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| ``@author`` | The name of the author | contract, interface, function |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| ``@notice`` | Explanation of functionality | contract, interface, function |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| ``@dev`` | Any extra details | contract, interface, function |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| ``@param`` | Parameter type followed by parameter name | function |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
| ``@return`` | The return value of a contract's function | function |
|
||||
+-------------+-------------------------------------------+-------------------------------+
|
||||
|
@ -19,6 +19,7 @@ on its type. To handle any unexpected values, you should use the :ref:`revert fu
|
||||
tuple with a second `bool` value denoting success.
|
||||
|
||||
.. index:: ! value type, ! type;value
|
||||
.. _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
|
||||
|
||||
.. _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
|
||||
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:
|
||||
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
|
||||
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.
|
||||
In contrast, 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.
|
||||
|
||||
* 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 are also visible in all other memory variables that refer to the same data.
|
||||
* Assignments from ``storage`` to a local storage variable also only assign a reference.
|
||||
* 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.
|
||||
|
||||
::
|
||||
|
||||
@ -805,40 +811,50 @@ if the local variable itself is just a reference.
|
||||
Arrays
|
||||
------
|
||||
|
||||
Arrays can have a compile-time fixed size or they can be dynamic.
|
||||
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>`.
|
||||
Arrays can have a compile-time fixed size, or they can have a dynamic size.
|
||||
|
||||
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
|
||||
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).
|
||||
The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
|
||||
and an array of dynamic size as ``T[]``.
|
||||
|
||||
Accessing an array past its end causes a revert. If you want to add
|
||||
new elements, you have to use ``.push()`` or increase the ``.length``
|
||||
member (see below).
|
||||
For example, an array of 5 dynamic arrays of ``uint`` is written as
|
||||
``uint[][5]``. The notation is reversed compared to some other languages. In
|
||||
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[]``,
|
||||
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
|
||||
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``
|
||||
for arbitrary-length string (UTF-8) data. If you can limit the length to a certain
|
||||
number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper.
|
||||
|
||||
You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule,
|
||||
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
|
||||
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::
|
||||
If you want to access the byte-representation of a string ``s``, use
|
||||
``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind
|
||||
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>`.
|
||||
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
|
||||
|
||||
@ -912,8 +928,10 @@ complications because of how arrays are passed in the ABI.
|
||||
|
||||
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
|
||||
|
||||
Members
|
||||
^^^^^^^
|
||||
.. _array-members:
|
||||
|
||||
Array Members
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
**length**:
|
||||
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``.
|
||||
|
||||
.. index:: !mapping
|
||||
.. _mapping-types:
|
||||
|
||||
Mappings
|
||||
--------
|
||||
Mapping Types
|
||||
=============
|
||||
|
||||
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
||||
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 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``.
|
||||
|
||||
|
@ -200,15 +200,27 @@ Input Description
|
||||
"MyLib": "0x123123..."
|
||||
}
|
||||
}
|
||||
// The following can be used to select desired outputs.
|
||||
// If this field is omitted, then the compiler loads and does type checking, but will not generate any outputs apart from errors.
|
||||
// The first level key is the file name and the second is the contract name, where empty contract name refers to the file itself,
|
||||
// while the star refers to all of the contracts.
|
||||
// The following can be used to select desired outputs based
|
||||
// on file and contract names.
|
||||
// If this field is omitted, then the compiler loads and does type checking,
|
||||
// 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:
|
||||
// abi - ABI
|
||||
//
|
||||
// File level (needs empty string as contract name):
|
||||
// ast - 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)
|
||||
// userdoc - User documentation (natspec)
|
||||
// metadata - Metadata
|
||||
@ -281,7 +293,7 @@ Output Description
|
||||
// This contains the file-level outputs. In can be limited/filtered by the outputSelection settings.
|
||||
sources: {
|
||||
"sourceFile.sol": {
|
||||
// Identifier (used in source maps)
|
||||
// Identifier of the source (used in source maps)
|
||||
id: 1,
|
||||
// The AST object
|
||||
ast: {},
|
||||
|
@ -1,18 +1,36 @@
|
||||
set(sources
|
||||
Algorithms.h
|
||||
Assertions.h
|
||||
boost_multiprecision_number_compare_bug_workaround.hpp
|
||||
Common.h
|
||||
CommonData.cpp
|
||||
CommonData.h
|
||||
CommonIO.cpp
|
||||
CommonIO.h
|
||||
Exceptions.cpp
|
||||
Exceptions.h
|
||||
FixedHash.h
|
||||
IndentedWriter.cpp
|
||||
IndentedWriter.h
|
||||
JSON.cpp
|
||||
JSON.h
|
||||
Keccak256.cpp
|
||||
Keccak256.h
|
||||
Result.h
|
||||
StringUtils.cpp
|
||||
StringUtils.h
|
||||
SwarmHash.cpp
|
||||
SwarmHash.h
|
||||
UTF8.cpp
|
||||
UTF8.h
|
||||
vector_ref.h
|
||||
Visitor.h
|
||||
Whiskers.cpp
|
||||
Whiskers.h
|
||||
)
|
||||
|
||||
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 SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
add_dependencies(devcore solidity_BuildInfo.h)
|
||||
|
@ -29,6 +29,28 @@
|
||||
using namespace std;
|
||||
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)
|
||||
{
|
||||
if (_i >= '0' && _i <= '9')
|
||||
|
@ -50,18 +50,18 @@ enum class HexPrefix
|
||||
DontAdd = 0,
|
||||
Add = 1,
|
||||
};
|
||||
|
||||
enum class HexCase
|
||||
{
|
||||
Lower = 0,
|
||||
Upper = 1,
|
||||
Mixed = 2,
|
||||
};
|
||||
|
||||
/// 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.
|
||||
/// @example toHex("A\x69") == "4169"
|
||||
template <class T>
|
||||
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();
|
||||
}
|
||||
std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower);
|
||||
|
||||
/// Converts a (printable) ASCII hex character into the correspnding integer value.
|
||||
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
|
||||
@ -153,7 +153,7 @@ inline std::string formatNumber(bigint const& _value)
|
||||
if (_value < 0)
|
||||
return "-" + formatNumber(-_value);
|
||||
if (_value > 0x1000000)
|
||||
return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
|
||||
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
|
||||
else
|
||||
return _value.str();
|
||||
}
|
||||
@ -161,7 +161,7 @@ inline std::string formatNumber(bigint const& _value)
|
||||
inline std::string formatNumber(u256 const& _value)
|
||||
{
|
||||
if (_value > 0x1000000)
|
||||
return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
|
||||
return toHex(toCompactBigEndian(_value), HexPrefix::Add);
|
||||
else
|
||||
return _value.str();
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public:
|
||||
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
|
||||
|
||||
/// @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.
|
||||
bytesRef ref() { return bytesRef(m_data.data(), N); }
|
||||
|
@ -36,7 +36,7 @@ string IndentedWriter::format() const
|
||||
void IndentedWriter::newLine()
|
||||
{
|
||||
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()
|
||||
|
@ -34,8 +34,6 @@ DEV_SIMPLE_EXCEPTION(IndentedWriterError);
|
||||
class IndentedWriter
|
||||
{
|
||||
public:
|
||||
explicit IndentedWriter(): m_lines(std::vector<Line>{{std::string(), 0}}) {}
|
||||
|
||||
// Returns the formatted output.
|
||||
std::string format() const;
|
||||
|
||||
@ -61,7 +59,7 @@ private:
|
||||
unsigned indentation;
|
||||
};
|
||||
|
||||
std::vector<Line> m_lines;
|
||||
std::vector<Line> m_lines{{std::string(), 0}};
|
||||
};
|
||||
|
||||
}
|
||||
|
66
libdevcore/Result.h
Normal file
66
libdevcore/Result.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
/// Simple generic result that holds a value and an optional error message.
|
||||
/// Results can be implicitly converted to and created from the type of
|
||||
/// the value they hold. The class is mainly designed for a result type of
|
||||
/// bool or pointer type. The idea is that the default constructed value of
|
||||
/// the result type is interpreted as an error value.
|
||||
///
|
||||
/// Result<bool> check()
|
||||
/// {
|
||||
/// if (false)
|
||||
/// return Result<bool>("Error message.")
|
||||
/// return true;
|
||||
/// }
|
||||
///
|
||||
|
||||
template <class ResultType>
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result(ResultType _value): Result(_value, std::string{}) {}
|
||||
Result(std::string _message): Result(ResultType{}, std::move(_message)) {}
|
||||
|
||||
/// @{
|
||||
/// @name Wrapper functions
|
||||
/// Wrapper functions that provide implicit conversions to and explicit retrieval of
|
||||
/// the value this result holds.
|
||||
operator ResultType const&() const { return m_value; }
|
||||
ResultType const& get() const { return m_value; }
|
||||
/// @}
|
||||
|
||||
/// @returns the error message (can be empty).
|
||||
std::string const& message() const { return m_message; }
|
||||
|
||||
private:
|
||||
explicit Result(ResultType _value, std::string _message):
|
||||
m_value(std::move(_value)),
|
||||
m_message(std::move(_message))
|
||||
{}
|
||||
|
||||
ResultType m_value;
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
}
|
@ -91,7 +91,7 @@ string dev::quotedAlternativesList(vector<string> const& suggestions)
|
||||
vector<string> quotedSuggestions;
|
||||
|
||||
for (auto& suggestion: suggestions)
|
||||
quotedSuggestions.push_back("\"" + suggestion + "\"");
|
||||
quotedSuggestions.emplace_back("\"" + suggestion + "\"");
|
||||
|
||||
return joinHumanReadable(quotedSuggestions, ", ", " or ");
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
@ -72,4 +74,84 @@ std::string joinHumanReadable
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Formats large numbers to be easily readable by humans.
|
||||
/// Returns decimal representation for smaller numbers; hex for large numbers.
|
||||
/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
|
||||
/// formulaic form like 0x01 * 2**24 - 1.
|
||||
/// @a T will typically by unsigned, u160, u256 or bigint.
|
||||
/// @param _value to be formatted
|
||||
/// @param _useTruncation if true, internal truncation is also applied,
|
||||
/// like 0x5555...{+56 more}...5555
|
||||
/// @example formatNumber((u256)0x7ffffff)
|
||||
template <class T>
|
||||
inline std::string formatNumberReadable(
|
||||
T const& _value,
|
||||
bool _useTruncation = false
|
||||
)
|
||||
{
|
||||
static_assert(
|
||||
std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed,
|
||||
"only unsigned types or bigint supported"
|
||||
); //bigint does not carry sign bit on shift
|
||||
|
||||
// smaller numbers return as decimal
|
||||
if (_value <= 0x1000000)
|
||||
return _value.str();
|
||||
|
||||
HexCase hexcase = HexCase::Mixed;
|
||||
HexPrefix prefix = HexPrefix::Add;
|
||||
|
||||
// when multiple trailing zero bytes, format as N * 2**x
|
||||
int i = 0;
|
||||
T v = _value;
|
||||
for (; (v & 0xff) == 0; v >>= 8)
|
||||
++i;
|
||||
if (i > 2)
|
||||
{
|
||||
// 0x100 yields 2**8 (N is 1 and redundant)
|
||||
if (v == 1)
|
||||
return "2**" + std::to_string(i * 8);
|
||||
return toHex(toCompactBigEndian(v), prefix, hexcase) +
|
||||
" * 2**" +
|
||||
std::to_string(i * 8);
|
||||
}
|
||||
|
||||
// when multiple trailing FF bytes, format as N * 2**x - 1
|
||||
i = 0;
|
||||
for (v = _value; (v & 0xff) == 0xff; v >>= 8)
|
||||
++i;
|
||||
if (i > 2)
|
||||
{
|
||||
// 0xFF yields 2**8 - 1 (v is 0 in that case)
|
||||
if (v == 0)
|
||||
return "2**" + std::to_string(i * 8) + " - 1";
|
||||
return toHex(toCompactBigEndian(T(v + 1)), prefix, hexcase) +
|
||||
" * 2**" + std::to_string(i * 8) +
|
||||
" - 1";
|
||||
}
|
||||
|
||||
std::string str = toHex(toCompactBigEndian(_value), prefix, hexcase);
|
||||
if (_useTruncation)
|
||||
{
|
||||
// return as interior-truncated hex.
|
||||
int len = str.size();
|
||||
|
||||
if (len < 24)
|
||||
return str;
|
||||
|
||||
const int initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
|
||||
const int finalChars = 4;
|
||||
int numSkipped = len - initialChars - finalChars;
|
||||
|
||||
return str.substr(0, initialChars) +
|
||||
"...{+" +
|
||||
std::to_string(numSkipped) +
|
||||
" more}..." +
|
||||
str.substr(len-finalChars, len);
|
||||
}
|
||||
|
||||
// otherwise, show whole value.
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
||||
{
|
||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||
m_deposit += _i.deposit();
|
||||
m_items.push_back(_i);
|
||||
m_items.emplace_back(_i);
|
||||
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
||||
m_items.back().setLocation(m_currentSourceLocation);
|
||||
return back();
|
||||
@ -353,14 +353,14 @@ AssemblyItem Assembly::namedTag(string const& _name)
|
||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||
if (!m_namedTags.count(_name))
|
||||
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)
|
||||
{
|
||||
h256 h(dev::keccak256(_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)
|
||||
@ -415,14 +415,14 @@ map<u256, u256> Assembly::optimiseInternal(
|
||||
|
||||
if (_settings.runJumpdestRemover)
|
||||
{
|
||||
JumpdestRemover jumpdestOpt(m_items);
|
||||
JumpdestRemover jumpdestOpt{m_items};
|
||||
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
|
||||
count++;
|
||||
}
|
||||
|
||||
if (_settings.runPeephole)
|
||||
{
|
||||
PeepholeOptimiser peepOpt(m_items);
|
||||
PeepholeOptimiser peepOpt{m_items};
|
||||
while (peepOpt.optimise())
|
||||
{
|
||||
count++;
|
||||
@ -433,7 +433,7 @@ map<u256, u256> Assembly::optimiseInternal(
|
||||
// This only modifies PushTags, we have to run again to actually remove code.
|
||||
if (_settings.runDeduplicate)
|
||||
{
|
||||
BlockDeduplicator dedup(m_items);
|
||||
BlockDeduplicator dedup{m_items};
|
||||
if (dedup.deduplicate())
|
||||
{
|
||||
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.
|
||||
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();
|
||||
while (iter != m_items.end())
|
||||
{
|
||||
KnownState emptyState;
|
||||
CommonSubexpressionEliminator eliminator(emptyState);
|
||||
CommonSubexpressionEliminator eliminator{emptyState};
|
||||
auto orig = iter;
|
||||
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
||||
bool shouldReplace = false;
|
||||
|
@ -45,8 +45,6 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
|
||||
class Assembly
|
||||
{
|
||||
public:
|
||||
Assembly() {}
|
||||
|
||||
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, 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.
|
||||
|
@ -110,7 +110,8 @@ int AssemblyItem::returnValues() const
|
||||
return 1;
|
||||
case Tag:
|
||||
return 0;
|
||||
default:;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -135,9 +136,10 @@ bool AssemblyItem::canBeFunctional() const
|
||||
return true;
|
||||
case Tag:
|
||||
return false;
|
||||
default:;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
string AssemblyItem::getJumpTypeAsString() const
|
||||
@ -168,7 +170,7 @@ string AssemblyItem::toAssemblyText() const
|
||||
break;
|
||||
}
|
||||
case Push:
|
||||
text = toHex(toCompactBigEndian(data(), 1), 1, HexPrefix::Add);
|
||||
text = toHex(toCompactBigEndian(data(), 1), HexPrefix::Add);
|
||||
break;
|
||||
case PushString:
|
||||
text = string("data_") + toHex(data());
|
||||
|
@ -57,22 +57,26 @@ class AssemblyItem
|
||||
public:
|
||||
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
||||
|
||||
AssemblyItem(u256 _push, langutil::SourceLocation const& _location = langutil::SourceLocation()):
|
||||
AssemblyItem(Push, _push, _location) { }
|
||||
AssemblyItem(solidity::Instruction _i, langutil::SourceLocation const& _location = langutil::SourceLocation()):
|
||||
AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||
AssemblyItem(Push, std::move(_push), std::move(_location)) { }
|
||||
AssemblyItem(solidity::Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||
m_type(Operation),
|
||||
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_location(_location)
|
||||
m_location(std::move(_location))
|
||||
{
|
||||
if (m_type == Operation)
|
||||
m_instruction = Instruction(uint8_t(_data));
|
||||
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 pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
|
||||
@ -114,6 +118,13 @@ public:
|
||||
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
|
||||
/// the value of a jump tag takes @a _addressLength bytes.
|
||||
unsigned bytesRequired(unsigned _addressLength) const;
|
||||
|
@ -41,7 +41,7 @@ bool BlockDeduplicator::deduplicate()
|
||||
|
||||
// Virtual tag that signifies "the current block" and which is used to optimise loops.
|
||||
// We abort if this virtual tag actually exists.
|
||||
AssemblyItem pushSelf(PushTag, u256(-4));
|
||||
AssemblyItem pushSelf{PushTag, u256(-4)};
|
||||
if (
|
||||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
|
||||
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
|
||||
// block's own tag.
|
||||
AssemblyItem pushFirstTag(pushSelf);
|
||||
AssemblyItem pushSecondTag(pushSelf);
|
||||
AssemblyItem pushFirstTag{pushSelf};
|
||||
AssemblyItem pushSecondTag{pushSelf};
|
||||
|
||||
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
|
||||
pushFirstTag = m_items.at(_i).pushTag();
|
||||
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
|
||||
pushSecondTag = m_items.at(_j).pushTag();
|
||||
|
||||
BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
|
||||
BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
|
||||
BlockIterator end(m_items.end(), m_items.end());
|
||||
BlockIterator first{m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf};
|
||||
BlockIterator second{m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf};
|
||||
BlockIterator end{m_items.end(), m_items.end()};
|
||||
|
||||
if (first != end && (*first).type() == Tag)
|
||||
++first;
|
||||
@ -126,7 +126,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
|
||||
{
|
||||
if (it == end)
|
||||
return *this;
|
||||
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(Instruction::JUMPI))
|
||||
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI})
|
||||
it = end;
|
||||
else
|
||||
{
|
||||
|
@ -1,20 +1,38 @@
|
||||
set(sources
|
||||
Assembly.cpp
|
||||
Assembly.h
|
||||
AssemblyItem.cpp
|
||||
AssemblyItem.h
|
||||
BlockDeduplicator.cpp
|
||||
BlockDeduplicator.h
|
||||
CommonSubexpressionEliminator.cpp
|
||||
CommonSubexpressionEliminator.h
|
||||
ConstantOptimiser.cpp
|
||||
ConstantOptimiser.h
|
||||
ControlFlowGraph.cpp
|
||||
ControlFlowGraph.h
|
||||
Exceptions.h
|
||||
ExpressionClasses.cpp
|
||||
ExpressionClasses.h
|
||||
GasMeter.cpp
|
||||
GasMeter.h
|
||||
Instruction.cpp
|
||||
Instruction.h
|
||||
JumpdestRemover.cpp
|
||||
JumpdestRemover.h
|
||||
KnownState.cpp
|
||||
KnownState.h
|
||||
LinkerObject.cpp
|
||||
LinkerObject.h
|
||||
PathGasMeter.cpp
|
||||
PathGasMeter.h
|
||||
PeepholeOptimiser.cpp
|
||||
PeepholeOptimiser.h
|
||||
SemanticInformation.cpp
|
||||
SemanticInformation.h
|
||||
SimplificationRule.h
|
||||
SimplificationRules.cpp
|
||||
SimplificationRules.h
|
||||
)
|
||||
|
||||
add_library(evmasm ${sources})
|
||||
|
@ -87,7 +87,7 @@ void ControlFlowGraph::splitBlocks()
|
||||
m_blocks[id].begin = index;
|
||||
}
|
||||
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))
|
||||
{
|
||||
m_blocks[id].end = index + 1;
|
||||
|
@ -304,7 +304,7 @@ KnownState::StoreOperation KnownState::storeInStorage(
|
||||
|
||||
AssemblyItem item(Instruction::SSTORE, _location);
|
||||
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;
|
||||
// increment a second time so that we get unique sequence numbers for writes
|
||||
m_sequenceNumber++;
|
||||
@ -336,7 +336,7 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source
|
||||
|
||||
AssemblyItem item(Instruction::MSTORE, _location);
|
||||
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;
|
||||
// increment a second time so that we get unique sequence numbers for writes
|
||||
m_sequenceNumber++;
|
||||
|
@ -74,18 +74,13 @@ public:
|
||||
struct StoreOperation
|
||||
{
|
||||
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; }
|
||||
Target target;
|
||||
Id slot;
|
||||
unsigned sequenceNumber;
|
||||
Id expression;
|
||||
|
||||
Target target{Invalid};
|
||||
Id slot{std::numeric_limits<Id>::max()};
|
||||
unsigned sequenceNumber{std::numeric_limits<unsigned>::max()};
|
||||
Id expression{std::numeric_limits<Id>::max()};
|
||||
};
|
||||
|
||||
explicit KnownState(
|
||||
|
@ -160,8 +160,7 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2>
|
||||
{
|
||||
// Remove SWAP1 if following instruction is commutative
|
||||
if (
|
||||
_swap.type() == Operation &&
|
||||
_swap.instruction() == Instruction::SWAP1 &&
|
||||
_swap == Instruction::SWAP1 &&
|
||||
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)
|
||||
{
|
||||
map<Instruction, Instruction> swappableOps{
|
||||
static map<Instruction, Instruction> const swappableOps{
|
||||
{ Instruction::LT, Instruction::GT },
|
||||
{ Instruction::GT, Instruction::LT },
|
||||
{ Instruction::SLT, Instruction::SGT },
|
||||
@ -185,8 +184,7 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
|
||||
};
|
||||
|
||||
if (
|
||||
_swap.type() == Operation &&
|
||||
_swap.instruction() == Instruction::SWAP1 &&
|
||||
_swap == Instruction::SWAP1 &&
|
||||
_op.type() == Operation &&
|
||||
swappableOps.count(_op.instruction())
|
||||
)
|
||||
|
@ -108,7 +108,7 @@ bool SemanticInformation::isSwapInstruction(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)
|
||||
|
@ -209,7 +209,7 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation c
|
||||
item = _pattern.toAssemblyItem(_location);
|
||||
}
|
||||
for (auto const& arg: _pattern.arguments())
|
||||
arguments.push_back(ExpressionTemplate(arg, _location));
|
||||
arguments.emplace_back(arg, _location);
|
||||
}
|
||||
|
||||
string ExpressionTemplate::toString() const
|
||||
|
@ -1,12 +1,24 @@
|
||||
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
|
||||
set(sources
|
||||
CharStream.cpp
|
||||
CharStream.h
|
||||
ErrorReporter.cpp
|
||||
ErrorReporter.h
|
||||
EVMVersion.h
|
||||
Exceptions.cpp
|
||||
Exceptions.h
|
||||
ParserBase.cpp
|
||||
ParserBase.h
|
||||
Scanner.cpp
|
||||
Scanner.h
|
||||
SourceLocation.h
|
||||
SourceReferenceExtractor.cpp
|
||||
SourceReferenceExtractor.h
|
||||
SourceReferenceFormatter.cpp
|
||||
SourceReferenceFormatter.h
|
||||
Token.cpp
|
||||
Token.h
|
||||
UndefMacros.h
|
||||
)
|
||||
|
||||
add_library(langutil ${sources})
|
||||
|
@ -67,9 +67,9 @@ namespace langutil
|
||||
class CharStream
|
||||
{
|
||||
public:
|
||||
CharStream(): m_position(0) {}
|
||||
CharStream() = default;
|
||||
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; }
|
||||
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
|
||||
@ -94,7 +94,7 @@ public:
|
||||
private:
|
||||
std::string m_source;
|
||||
std::string m_name;
|
||||
size_t m_position;
|
||||
size_t m_position{0};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class EVMVersion:
|
||||
boost::equality_comparable<EVMVersion>
|
||||
{
|
||||
public:
|
||||
EVMVersion() {}
|
||||
EVMVersion() = default;
|
||||
|
||||
static EVMVersion homestead() { return {Version::Homestead}; }
|
||||
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
|
||||
|
@ -105,7 +105,7 @@ class SecondarySourceLocation
|
||||
public:
|
||||
SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
|
||||
{
|
||||
infos.push_back(std::make_pair(_errMsg, _sourceLocation));
|
||||
infos.emplace_back(_errMsg, _sourceLocation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -100,10 +100,10 @@ void ParserBase::decreaseRecursionDepth()
|
||||
|
||||
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)
|
||||
{
|
||||
m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), source()), _description);
|
||||
m_errorReporter.fatalParserError(SourceLocation{position(), endPosition(), source()}, _description);
|
||||
}
|
||||
|
@ -38,10 +38,6 @@ namespace langutil
|
||||
*/
|
||||
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
|
||||
{
|
||||
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; }
|
||||
|
||||
int start;
|
||||
int end;
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
std::shared_ptr<CharStream> source;
|
||||
};
|
||||
|
||||
|
89
liblangutil/SourceReferenceExtractor.cpp
Normal file
89
liblangutil/SourceReferenceExtractor.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <liblangutil/SourceReferenceExtractor.h>
|
||||
#include <liblangutil/CharStream.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace langutil;
|
||||
|
||||
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
|
||||
{
|
||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||
|
||||
string const* message = boost::get_error_info<errinfo_comment>(_exception);
|
||||
SourceReference primary = extract(location, message ? *message : "");
|
||||
|
||||
std::vector<SourceReference> secondary;
|
||||
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
||||
if (secondaryLocation && !secondaryLocation->infos.empty())
|
||||
for (auto const& info: secondaryLocation->infos)
|
||||
secondary.emplace_back(extract(&info.second, info.first));
|
||||
|
||||
return Message{std::move(primary), _category, std::move(secondary)};
|
||||
}
|
||||
|
||||
SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message)
|
||||
{
|
||||
if (!_location || !_location->source.get()) // Nothing we can extract here
|
||||
return SourceReference::MessageOnly(std::move(message));
|
||||
|
||||
shared_ptr<CharStream> const& source = _location->source;
|
||||
|
||||
LineColumn const interest = source->translatePositionToLineColumn(_location->start);
|
||||
LineColumn start = interest;
|
||||
LineColumn end = source->translatePositionToLineColumn(_location->end);
|
||||
bool const isMultiline = start.line != end.line;
|
||||
|
||||
string line = source->lineAtPosition(_location->start);
|
||||
|
||||
int locationLength = isMultiline ? line.length() - start.column : end.column - start.column;
|
||||
if (locationLength > 150)
|
||||
{
|
||||
line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35);
|
||||
end.column = start.column + 75;
|
||||
locationLength = 75;
|
||||
}
|
||||
|
||||
if (line.length() > 150)
|
||||
{
|
||||
int const len = line.length();
|
||||
line = line.substr(max(0, start.column - 35), min(start.column, 35) + min(locationLength + 35, len - start.column));
|
||||
if (start.column + locationLength + 35 < len)
|
||||
line += " ...";
|
||||
if (start.column > 35)
|
||||
{
|
||||
line = " ... " + line;
|
||||
start.column = 40;
|
||||
}
|
||||
end.column = start.column + locationLength;
|
||||
}
|
||||
|
||||
return SourceReference{
|
||||
std::move(message),
|
||||
source->name(),
|
||||
interest,
|
||||
isMultiline,
|
||||
line,
|
||||
start.column,
|
||||
end.column,
|
||||
};
|
||||
}
|
75
liblangutil/SourceReferenceExtractor.h
Normal file
75
liblangutil/SourceReferenceExtractor.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
struct Exception;
|
||||
}
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
|
||||
struct LineColumn
|
||||
{
|
||||
int line = {-1};
|
||||
int column = {-1};
|
||||
|
||||
LineColumn() = default;
|
||||
LineColumn(std::tuple<int, int> const& _t): line{std::get<0>(_t)}, column{std::get<1>(_t)} {}
|
||||
};
|
||||
|
||||
struct SourceReference
|
||||
{
|
||||
std::string message; ///< A message that relates to this source reference (such as a warning or an error message).
|
||||
std::string sourceName; ///< Underlying source name (for example the filename).
|
||||
LineColumn position; ///< Actual (error) position this source reference is surrounding.
|
||||
bool multiline = {false}; ///< Indicates whether the actual SourceReference is truncated to one line.
|
||||
std::string text; ///< Extracted source code text (potentially truncated if multiline or too long).
|
||||
int startColumn = {-1}; ///< Highlighting range-start of text field.
|
||||
int endColumn = {-1}; ///< Highlighting range-end of text field.
|
||||
|
||||
/// Constructs a SourceReference containing a message only.
|
||||
static SourceReference MessageOnly(std::string _msg)
|
||||
{
|
||||
SourceReference sref;
|
||||
sref.message = std::move(_msg);
|
||||
return sref;
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceLocation;
|
||||
|
||||
namespace SourceReferenceExtractor
|
||||
{
|
||||
struct Message
|
||||
{
|
||||
SourceReference primary;
|
||||
std::string category; // "Error", "Warning", ...
|
||||
std::vector<SourceReference> secondary;
|
||||
};
|
||||
|
||||
Message extract(dev::Exception const& _exception, std::string _category);
|
||||
SourceReference extract(SourceLocation const* _location, std::string message = "");
|
||||
}
|
||||
|
||||
}
|
@ -30,100 +30,63 @@ using namespace langutil;
|
||||
|
||||
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
||||
{
|
||||
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
|
||||
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)
|
||||
|
||||
if (!_ref.multiline)
|
||||
{
|
||||
string line = scanner.lineAtPosition(_location->start);
|
||||
|
||||
int locationLength = endColumn - startColumn;
|
||||
if (locationLength > 150)
|
||||
{
|
||||
line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35);
|
||||
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;
|
||||
m_stream << _ref.text << endl;
|
||||
|
||||
// mark the text-range like this: ^-----^
|
||||
for_each(
|
||||
line.cbegin(),
|
||||
line.cbegin() + startColumn,
|
||||
[this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
||||
_ref.text.cbegin(),
|
||||
_ref.text.cbegin() + _ref.startColumn,
|
||||
[this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
|
||||
);
|
||||
m_stream << "^";
|
||||
if (endColumn > startColumn + 2)
|
||||
m_stream << string(endColumn - startColumn - 2, '-');
|
||||
if (endColumn > startColumn + 1)
|
||||
if (_ref.endColumn > _ref.startColumn + 2)
|
||||
m_stream << string(_ref.endColumn - _ref.startColumn - 2, '-');
|
||||
if (_ref.endColumn > _ref.startColumn + 1)
|
||||
m_stream << "^";
|
||||
m_stream << endl;
|
||||
}
|
||||
else
|
||||
m_stream <<
|
||||
scanner.lineAtPosition(_location->start) <<
|
||||
_ref.text <<
|
||||
endl <<
|
||||
string(startColumn, ' ') <<
|
||||
string(_ref.startColumn, ' ') <<
|
||||
"^ (Relevant source part starts here and spans across multiple lines)." <<
|
||||
endl;
|
||||
}
|
||||
|
||||
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
|
||||
void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
|
||||
{
|
||||
if (!_location || !_location->source)
|
||||
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);
|
||||
m_stream << _location->source->name() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
|
||||
if (_ref.position.line != -1)
|
||||
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
|
||||
}
|
||||
|
||||
void SourceReferenceFormatter::printExceptionInformation(
|
||||
dev::Exception const& _exception,
|
||||
string const& _name
|
||||
)
|
||||
void SourceReferenceFormatter::printExceptionInformation(dev::Exception const& _error, std::string const& _category)
|
||||
{
|
||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||
auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
|
||||
printExceptionInformation(SourceReferenceExtractor::extract(_error, _category));
|
||||
}
|
||||
|
||||
printSourceName(location);
|
||||
void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg)
|
||||
{
|
||||
printSourceName(_msg.primary);
|
||||
|
||||
m_stream << _name;
|
||||
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
|
||||
m_stream << ": " << *description << endl;
|
||||
else
|
||||
m_stream << endl;
|
||||
m_stream << _msg.category << ": " << _msg.primary.message << endl;
|
||||
|
||||
printSourceLocation(location);
|
||||
printSourceLocation(_msg.primary);
|
||||
|
||||
if (secondarylocation && !secondarylocation->infos.empty())
|
||||
for (auto const& ref: _msg.secondary)
|
||||
{
|
||||
for (auto info: secondarylocation->infos)
|
||||
{
|
||||
printSourceName(&info.second);
|
||||
m_stream << info.first << endl;
|
||||
printSourceLocation(&info.second);
|
||||
}
|
||||
m_stream << endl;
|
||||
printSourceName(ref);
|
||||
m_stream << ref.message << endl;
|
||||
printSourceLocation(ref);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <liblangutil/SourceReferenceExtractor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -39,38 +40,33 @@ class Scanner;
|
||||
class SourceReferenceFormatter
|
||||
{
|
||||
public:
|
||||
using ScannerFromSourceNameFun = std::function<langutil::Scanner const&(std::string const&)>;
|
||||
|
||||
explicit SourceReferenceFormatter(
|
||||
std::ostream& _stream,
|
||||
ScannerFromSourceNameFun _scannerFromSourceName
|
||||
):
|
||||
m_stream(_stream),
|
||||
m_scannerFromSourceName(std::move(_scannerFromSourceName))
|
||||
explicit SourceReferenceFormatter(std::ostream& _stream):
|
||||
m_stream(_stream)
|
||||
{}
|
||||
|
||||
/// Prints source location if it is given.
|
||||
void printSourceLocation(langutil::SourceLocation const* _location);
|
||||
void printExceptionInformation(dev::Exception const& _exception, std::string const& _name);
|
||||
void printSourceLocation(SourceLocation const* _location);
|
||||
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(
|
||||
dev::Exception const& _exception,
|
||||
std::string const& _name,
|
||||
ScannerFromSourceNameFun const& _scannerFromSourceName
|
||||
std::string const& _name
|
||||
)
|
||||
{
|
||||
std::ostringstream errorOutput;
|
||||
|
||||
SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName);
|
||||
SourceReferenceFormatter formatter(errorOutput);
|
||||
formatter.printExceptionInformation(_exception, _name);
|
||||
return errorOutput.str();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Prints source name if location is given.
|
||||
void printSourceName(langutil::SourceLocation const* _location);
|
||||
void printSourceName(SourceReference const& _ref);
|
||||
|
||||
std::ostream& m_stream;
|
||||
ScannerFromSourceNameFun m_scannerFromSourceName;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
set(sources
|
||||
CodeFragment.cpp
|
||||
CodeFragment.h
|
||||
Compiler.cpp
|
||||
Compiler.h
|
||||
CompilerState.cpp
|
||||
CompilerState.h
|
||||
Exceptions.h
|
||||
Parser.cpp
|
||||
Parser.h
|
||||
)
|
||||
|
||||
add_library(lll ${sources})
|
||||
|
@ -353,7 +353,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
||||
if (j.tag() || j.which() != sp::utree_type::symbol_type)
|
||||
error<InvalidMacroArgs>();
|
||||
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)
|
||||
{
|
||||
@ -464,9 +464,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
||||
if (c++)
|
||||
{
|
||||
if (us == "LLL" && c == 1)
|
||||
code.push_back(CodeFragment(i, ns, m_readFile));
|
||||
code.emplace_back(i, ns, m_readFile);
|
||||
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 requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
|
||||
|
@ -41,7 +41,7 @@ class CodeFragment
|
||||
public:
|
||||
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);
|
||||
|
||||
static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile);
|
||||
|
@ -46,22 +46,22 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->push_back("Parse error.");
|
||||
_errors->push_back(boost::diagnostic_information(_e));
|
||||
_errors->emplace_back("Parse error.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->push_back("Parse exception.");
|
||||
_errors->push_back(boost::diagnostic_information(_e));
|
||||
_errors->emplace_back("Parse exception.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (_errors)
|
||||
_errors->push_back("Internal compiler exception.");
|
||||
_errors->emplace_back("Internal compiler exception.");
|
||||
}
|
||||
return bytes();
|
||||
}
|
||||
@ -84,22 +84,22 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->push_back("Parse error.");
|
||||
_errors->push_back(boost::diagnostic_information(_e));
|
||||
_errors->emplace_back("Parse error.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& _e)
|
||||
{
|
||||
if (_errors)
|
||||
{
|
||||
_errors->push_back("Parse exception.");
|
||||
_errors->push_back(boost::diagnostic_information(_e));
|
||||
_errors->emplace_back("Parse exception.");
|
||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (_errors)
|
||||
_errors->push_back("Internal compiler exception.");
|
||||
_errors->emplace_back("Internal compiler exception.");
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
if (EMSCRIPTEN)
|
||||
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)
|
||||
else()
|
||||
add_library(libsolc libsolc.cpp)
|
||||
add_library(libsolc libsolc.cpp libsolc.h)
|
||||
set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc)
|
||||
target_link_libraries(libsolc PRIVATE solidity)
|
||||
endif()
|
||||
|
@ -20,6 +20,8 @@
|
||||
* Public compiler API.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1,68 +1,121 @@
|
||||
# Until we have a clear separation, libyul has to be included here
|
||||
set(sources
|
||||
analysis/ConstantEvaluator.cpp
|
||||
analysis/ConstantEvaluator.h
|
||||
analysis/ContractLevelChecker.cpp
|
||||
analysis/ContractLevelChecker.h
|
||||
analysis/ControlFlowAnalyzer.cpp
|
||||
analysis/ControlFlowAnalyzer.h
|
||||
analysis/ControlFlowBuilder.cpp
|
||||
analysis/ControlFlowBuilder.h
|
||||
analysis/ControlFlowGraph.cpp
|
||||
analysis/ControlFlowGraph.h
|
||||
analysis/DeclarationContainer.cpp
|
||||
analysis/DeclarationContainer.h
|
||||
analysis/DocStringAnalyser.cpp
|
||||
analysis/DocStringAnalyser.h
|
||||
analysis/GlobalContext.cpp
|
||||
analysis/GlobalContext.h
|
||||
analysis/NameAndTypeResolver.cpp
|
||||
analysis/NameAndTypeResolver.h
|
||||
analysis/PostTypeChecker.cpp
|
||||
analysis/PostTypeChecker.h
|
||||
analysis/ReferencesResolver.cpp
|
||||
analysis/ReferencesResolver.h
|
||||
analysis/SemVerHandler.cpp
|
||||
analysis/SemVerHandler.h
|
||||
analysis/StaticAnalyzer.cpp
|
||||
analysis/StaticAnalyzer.h
|
||||
analysis/SyntaxChecker.cpp
|
||||
analysis/SyntaxChecker.h
|
||||
analysis/TypeChecker.cpp
|
||||
analysis/TypeChecker.h
|
||||
analysis/ViewPureChecker.cpp
|
||||
analysis/ViewPureChecker.h
|
||||
ast/AST.cpp
|
||||
ast/AST.h
|
||||
ast/AST_accept.h
|
||||
ast/ASTAnnotations.cpp
|
||||
ast/ASTAnnotations.h
|
||||
ast/ASTEnums.h
|
||||
ast/ASTForward.h
|
||||
ast/ASTJsonConverter.cpp
|
||||
ast/ASTJsonConverter.h
|
||||
ast/ASTPrinter.cpp
|
||||
ast/ASTPrinter.h
|
||||
ast/ASTVisitor.h
|
||||
ast/ExperimentalFeatures.h
|
||||
ast/Types.cpp
|
||||
ast/Types.h
|
||||
codegen/ABIFunctions.cpp
|
||||
codegen/ABIFunctions.h
|
||||
codegen/ArrayUtils.cpp
|
||||
codegen/ArrayUtils.h
|
||||
codegen/AsmCodeGen.cpp
|
||||
codegen/AsmCodeGen.h
|
||||
codegen/Compiler.cpp
|
||||
codegen/Compiler.h
|
||||
codegen/CompilerContext.cpp
|
||||
codegen/CompilerContext.h
|
||||
codegen/CompilerUtils.cpp
|
||||
codegen/CompilerUtils.h
|
||||
codegen/ContractCompiler.cpp
|
||||
codegen/ContractCompiler.h
|
||||
codegen/ExpressionCompiler.cpp
|
||||
codegen/ExpressionCompiler.h
|
||||
codegen/LValue.cpp
|
||||
codegen/LValue.h
|
||||
formal/SMTChecker.cpp
|
||||
formal/SMTChecker.h
|
||||
formal/SMTLib2Interface.cpp
|
||||
formal/SMTLib2Interface.h
|
||||
formal/SMTPortfolio.cpp
|
||||
formal/SMTPortfolio.h
|
||||
formal/SolverInterface.h
|
||||
formal/SSAVariable.cpp
|
||||
formal/SSAVariable.h
|
||||
formal/SymbolicTypes.cpp
|
||||
formal/SymbolicTypes.h
|
||||
formal/SymbolicVariables.cpp
|
||||
formal/SymbolicVariables.h
|
||||
formal/VariableUsage.cpp
|
||||
formal/VariableUsage.h
|
||||
interface/ABI.cpp
|
||||
interface/ABI.h
|
||||
interface/AssemblyStack.cpp
|
||||
interface/AssemblyStack.h
|
||||
interface/CompilerStack.cpp
|
||||
interface/CompilerStack.h
|
||||
interface/GasEstimator.cpp
|
||||
interface/GasEstimator.h
|
||||
interface/Natspec.cpp
|
||||
interface/Natspec.h
|
||||
interface/ReadFile.h
|
||||
interface/StandardCompiler.cpp
|
||||
interface/StandardCompiler.h
|
||||
interface/Version.cpp
|
||||
interface/Version.h
|
||||
parsing/DocStringParser.cpp
|
||||
parsing/DocStringParser.h
|
||||
parsing/Parser.cpp
|
||||
parsing/Parser.h
|
||||
parsing/Token.h
|
||||
)
|
||||
|
||||
find_package(Z3 QUIET)
|
||||
if (${Z3_FOUND})
|
||||
include_directories(${Z3_INCLUDE_DIR})
|
||||
add_definitions(-DHAVE_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()
|
||||
set(z3_SRCS)
|
||||
endif()
|
||||
|
||||
find_package(CVC4 QUIET)
|
||||
if (${CVC4_FOUND})
|
||||
include_directories(${CVC4_INCLUDE_DIR})
|
||||
add_definitions(-DHAVE_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()
|
||||
set(cvc4_SRCS)
|
||||
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})
|
||||
|
||||
if (${Z3_FOUND})
|
||||
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
|
||||
target_link_libraries(solidity PUBLIC Z3::Z3)
|
||||
endif()
|
||||
|
||||
if (${CVC4_FOUND})
|
||||
target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES})
|
||||
target_link_libraries(solidity PUBLIC CVC4::CVC4)
|
||||
endif()
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@ -41,7 +42,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
auto right = type(_operation.rightExpression());
|
||||
if (left && right)
|
||||
{
|
||||
auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||
if (!commonType)
|
||||
m_errorReporter.fatalTypeError(
|
||||
_operation.location(),
|
||||
|
@ -20,10 +20,9 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ContractLevelChecker.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
|
||||
@ -227,7 +226,7 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con
|
||||
return _type->hasEqualParameterTypes(*_funAndFlag.first);
|
||||
});
|
||||
if (it == overloads.end())
|
||||
overloads.push_back(make_pair(_type, _implemented));
|
||||
overloads.emplace_back(_type, _implemented);
|
||||
else if (it->second)
|
||||
{
|
||||
if (!_implemented)
|
||||
@ -409,8 +408,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
auto functionType = make_shared<FunctionType>(*f);
|
||||
// under non error circumstances this should be true
|
||||
if (functionType->interfaceFunctionType())
|
||||
externalDeclarations[functionType->externalSignature()].push_back(
|
||||
make_pair(f, functionType->asCallableFunction(false))
|
||||
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||
f, functionType->asCallableFunction(false)
|
||||
);
|
||||
}
|
||||
for (VariableDeclaration const* v: contract->stateVariables())
|
||||
@ -419,8 +418,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
auto functionType = make_shared<FunctionType>(*v);
|
||||
// under non error circumstances this should be true
|
||||
if (functionType->interfaceFunctionType())
|
||||
externalDeclarations[functionType->externalSignature()].push_back(
|
||||
make_pair(v, functionType->asCallableFunction(false))
|
||||
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||
v, functionType->asCallableFunction(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace langutil
|
||||
|
@ -16,7 +16,9 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <boost/range/algorithm/sort.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace langutil;
|
||||
@ -33,131 +35,112 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
|
||||
if (_function.isImplemented())
|
||||
{
|
||||
auto const& functionFlow = m_cfg.functionFlow(_function);
|
||||
checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit);
|
||||
checkUninitializedAccess(functionFlow.entry, functionFlow.exit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
set<VariableDeclaration const*> ControlFlowAnalyzer::variablesAssignedInNode(CFGNode const *node)
|
||||
void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const
|
||||
{
|
||||
set<VariableDeclaration const*> result;
|
||||
for (auto expression: node->block.expressions)
|
||||
struct NodeInfo
|
||||
{
|
||||
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;
|
||||
expressions.push(&assignment->leftHandSide());
|
||||
while (!expressions.empty())
|
||||
{
|
||||
Expression const* expression = expressions.top();
|
||||
expressions.pop();
|
||||
|
||||
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);
|
||||
}
|
||||
size_t previousUnassignedVariablesAtEntry = unassignedVariablesAtEntry.size();
|
||||
size_t previousUninitializedVariableAccessess = uninitializedVariableAccesses.size();
|
||||
unassignedVariablesAtEntry += _entryNode.unassignedVariablesAtExit;
|
||||
uninitializedVariableAccesses += _entryNode.uninitializedVariableAccesses;
|
||||
return
|
||||
unassignedVariablesAtEntry.size() > previousUnassignedVariablesAtEntry ||
|
||||
uninitializedVariableAccesses.size() > previousUninitializedVariableAccessess
|
||||
;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
map<CFGNode const*, NodeInfo> nodeInfos;
|
||||
set<CFGNode const*> nodesToTraverse;
|
||||
nodesToTraverse.insert(_entry);
|
||||
|
||||
void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
|
||||
FunctionDefinition const& _function,
|
||||
CFGNode const* _functionEntry,
|
||||
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
|
||||
// Walk all paths starting from the nodes in ``nodesToTraverse`` until ``NodeInfo::propagateFrom``
|
||||
// returns false for all exits, i.e. until all paths have been walked with maximal sets of unassigned
|
||||
// variables and accesses.
|
||||
while (!nodesToTraverse.empty())
|
||||
{
|
||||
auto node = nodesToTraverse.top();
|
||||
nodesToTraverse.pop();
|
||||
CFGNode const* currentNode = *nodesToTraverse.begin();
|
||||
nodesToTraverse.erase(nodesToTraverse.begin());
|
||||
|
||||
auto& unassignedAtNode = unassigned[node];
|
||||
|
||||
if (node->block.returnStatement != nullptr)
|
||||
if (node->block.returnStatement->expression())
|
||||
unassignedAtNode.clear();
|
||||
if (!unassignedAtNode.empty())
|
||||
auto& nodeInfo = nodeInfos[currentNode];
|
||||
auto unassignedVariables = nodeInfo.unassignedVariablesAtEntry;
|
||||
for (auto const& variableOccurrence: currentNode->variableOccurrences)
|
||||
{
|
||||
// kill all return values to which a value is assigned
|
||||
for (auto const* variableDeclaration: variablesAssignedInNode(node))
|
||||
unassignedAtNode.erase(variableDeclaration);
|
||||
|
||||
// kill all return values referenced in inline assembly
|
||||
// a reference is enough, checking whether there actually was an assignment might be overkill
|
||||
for (auto assembly: node->block.inlineAssemblyStatements)
|
||||
for (auto const& ref: assembly->annotation().externalReferences)
|
||||
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
|
||||
unassignedAtNode.erase(variableDeclaration);
|
||||
switch (variableOccurrence.kind())
|
||||
{
|
||||
case VariableOccurrence::Kind::Assignment:
|
||||
unassignedVariables.erase(&variableOccurrence.declaration());
|
||||
break;
|
||||
case VariableOccurrence::Kind::InlineAssembly:
|
||||
// We consider all variables referenced in inline assembly as accessed.
|
||||
// So far any reference is enough, but we might want to actually analyze
|
||||
// the control flow in the assembly at some point.
|
||||
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;
|
||||
case VariableOccurrence::Kind::Declaration:
|
||||
unassignedVariables.insert(&variableOccurrence.declaration());
|
||||
break;
|
||||
}
|
||||
}
|
||||
nodeInfo.unassignedVariablesAtExit = std::move(unassignedVariables);
|
||||
|
||||
for (auto const& exit: node->exits)
|
||||
{
|
||||
auto& unassignedAtExit = unassigned[exit];
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!unassigned[_functionExit].empty())
|
||||
auto const& exitInfo = nodeInfos[_exit];
|
||||
if (!exitInfo.uninitializedVariableAccesses.empty())
|
||||
{
|
||||
vector<VariableDeclaration const*> unassignedOrdered(
|
||||
unassigned[_functionExit].begin(),
|
||||
unassigned[_functionExit].end()
|
||||
);
|
||||
sort(
|
||||
unassignedOrdered.begin(),
|
||||
unassignedOrdered.end(),
|
||||
[](VariableDeclaration const* lhs, VariableDeclaration const* rhs) -> bool {
|
||||
return lhs->id() < rhs->id();
|
||||
vector<VariableOccurrence const*> uninitializedAccessesOrdered(
|
||||
exitInfo.uninitializedVariableAccesses.begin(),
|
||||
exitInfo.uninitializedVariableAccesses.end()
|
||||
);
|
||||
boost::range::sort(
|
||||
uninitializedAccessesOrdered,
|
||||
[](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool
|
||||
{
|
||||
return *lhs < *rhs;
|
||||
}
|
||||
);
|
||||
for (auto const* returnVal: unassignedOrdered)
|
||||
|
||||
for (auto const* variableOccurrence: uninitializedAccessesOrdered)
|
||||
{
|
||||
SecondarySourceLocation ssl;
|
||||
for (CFGNode* lastNodeBeforeExit: _functionExit->entries)
|
||||
if (unassigned[lastNodeBeforeExit].count(returnVal))
|
||||
{
|
||||
if (!!lastNodeBeforeExit->block.returnStatement)
|
||||
ssl.append("Problematic return:", lastNodeBeforeExit->block.returnStatement->location());
|
||||
else
|
||||
ssl.append("Problematic end of function:", _function.location());
|
||||
}
|
||||
if (variableOccurrence->occurrence())
|
||||
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
returnVal->location(),
|
||||
variableOccurrence->occurrence() ?
|
||||
variableOccurrence->occurrence()->location() :
|
||||
variableOccurrence->declaration().location(),
|
||||
ssl,
|
||||
"This variable is of storage pointer type and might be returned without assignment and "
|
||||
"could be used uninitialized. Assign the variable (potentially from itself) "
|
||||
"to fix this error."
|
||||
string("This variable is of storage pointer type and can be ") +
|
||||
(variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
|
||||
" without prior assignment."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
@ -37,12 +36,8 @@ public:
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
|
||||
private:
|
||||
static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
|
||||
void checkUnassignedStorageReturnValues(
|
||||
FunctionDefinition const& _function,
|
||||
CFGNode const* _functionEntry,
|
||||
CFGNode const* _functionExit
|
||||
) const;
|
||||
/// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
|
||||
void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const;
|
||||
|
||||
CFG const& m_cfg;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
@ -22,7 +22,10 @@ using namespace solidity;
|
||||
using namespace std;
|
||||
|
||||
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();
|
||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
||||
builder.appendControlFlow(_function);
|
||||
connect(builder.m_currentNode, functionFlow->exit);
|
||||
|
||||
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)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
@ -219,64 +204,24 @@ bool ControlFlowBuilder::visit(Continue const&)
|
||||
bool ControlFlowBuilder::visit(Throw const&)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
||||
solAssert(!!m_revertNode, "");
|
||||
connect(m_currentNode, m_revertNode);
|
||||
m_currentNode = newLabel();
|
||||
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&)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
auto modifierFlow = dynamic_cast<ModifierFlow const*>(&m_currentFunctionFlow);
|
||||
solAssert(!!modifierFlow, "");
|
||||
|
||||
connect(m_currentNode, modifierFlow->placeholderEntry);
|
||||
solAssert(!!m_placeholderEntry, "");
|
||||
solAssert(!!m_placeholderExit, "");
|
||||
|
||||
connect(m_currentNode, m_placeholderEntry);
|
||||
m_currentNode = newLabel();
|
||||
|
||||
connect(modifierFlow->placeholderExit, m_currentNode);
|
||||
connect(m_placeholderExit, m_currentNode);
|
||||
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)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
@ -286,19 +231,19 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||
switch (functionType->kind())
|
||||
{
|
||||
case FunctionType::Kind::Revert:
|
||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
||||
solAssert(!!m_revertNode, "");
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
||||
connect(m_currentNode, m_revertNode);
|
||||
m_currentNode = newLabel();
|
||||
return false;
|
||||
case FunctionType::Kind::Require:
|
||||
case FunctionType::Kind::Assert:
|
||||
{
|
||||
solAssert(!!m_currentFunctionFlow.revert, "");
|
||||
solAssert(!!m_revertNode, "");
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
connect(m_currentNode, m_currentFunctionFlow.revert);
|
||||
connect(m_currentNode, m_revertNode);
|
||||
auto nextNode = newLabel();
|
||||
connect(m_currentNode, nextNode);
|
||||
m_currentNode = nextNode;
|
||||
@ -310,6 +255,183 @@ bool ControlFlowBuilder::visit(FunctionCall const& _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)
|
||||
{
|
||||
_node.accept(*this);
|
||||
|
@ -38,14 +38,11 @@ public:
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionDefinition const& _function
|
||||
);
|
||||
static std::unique_ptr<ModifierFlow> createModifierFlow(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
ModifierDefinition const& _modifier
|
||||
);
|
||||
|
||||
private:
|
||||
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
|
||||
|
||||
// Visits for constructing the control flow.
|
||||
bool visit(BinaryOperation const& _operation) override;
|
||||
bool visit(Conditional const& _conditional) override;
|
||||
bool visit(IfStatement const& _ifStatement) override;
|
||||
@ -54,12 +51,20 @@ private:
|
||||
bool visit(Break const&) override;
|
||||
bool visit(Continue 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(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.
|
||||
void appendControlFlow(ASTNode const& _node);
|
||||
@ -73,9 +78,6 @@ private:
|
||||
static void connect(CFGNode* _from, CFGNode* _to);
|
||||
|
||||
|
||||
protected:
|
||||
bool visitNode(ASTNode const& node) override;
|
||||
|
||||
private:
|
||||
|
||||
/// Splits the control flow starting at the current node into n paths.
|
||||
@ -114,17 +116,18 @@ private:
|
||||
|
||||
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_returnNode = nullptr;
|
||||
CFGNode* m_revertNode = nullptr;
|
||||
|
||||
/// The current jump destination of break Statements.
|
||||
CFGNode* m_breakJump = nullptr;
|
||||
/// The current jump destination of continue Statements.
|
||||
CFGNode* m_continueJump = nullptr;
|
||||
|
||||
CFGNode* m_placeholderEntry = nullptr;
|
||||
CFGNode* m_placeholderExit = nullptr;
|
||||
|
||||
/// Helper class that replaces the break and continue jump destinations for the
|
||||
/// current scope and restores the originals at the end of the scope.
|
||||
class BreakContinueScope
|
||||
|
@ -16,10 +16,9 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
@ -29,20 +28,14 @@ using namespace dev::solidity;
|
||||
bool CFG::constructFlow(ASTNode const& _astRoot)
|
||||
{
|
||||
_astRoot.accept(*this);
|
||||
applyModifiers();
|
||||
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)
|
||||
{
|
||||
m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
||||
if (_function.isImplemented())
|
||||
m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -57,81 +50,3 @@ CFGNode* CFG::NodeContainer::newNode()
|
||||
m_nodes.emplace_back(new CFGNode());
|
||||
return m_nodes.back().get();
|
||||
}
|
||||
|
||||
void CFG::applyModifiers()
|
||||
{
|
||||
for (auto const& function: m_functionControlFlow)
|
||||
{
|
||||
for (auto const& modifierInvocation: boost::adaptors::reverse(function.first->modifiers()))
|
||||
{
|
||||
if (auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
||||
modifierInvocation->name()->annotation().referencedDeclaration
|
||||
))
|
||||
{
|
||||
solAssert(m_modifierControlFlow.count(modifierDefinition), "");
|
||||
applyModifierFlowToFunctionFlow(*m_modifierControlFlow[modifierDefinition], function.second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CFG::applyModifierFlowToFunctionFlow(
|
||||
ModifierFlow const& _modifierFlow,
|
||||
FunctionFlow* _functionFlow
|
||||
)
|
||||
{
|
||||
solAssert(!!_functionFlow, "");
|
||||
|
||||
map<CFGNode*, CFGNode*> copySrcToCopyDst;
|
||||
|
||||
// inherit the revert node of the function
|
||||
copySrcToCopyDst[_modifierFlow.revert] = _functionFlow->revert;
|
||||
|
||||
// replace the placeholder nodes by the function entry and exit
|
||||
copySrcToCopyDst[_modifierFlow.placeholderEntry] = _functionFlow->entry;
|
||||
copySrcToCopyDst[_modifierFlow.placeholderExit] = _functionFlow->exit;
|
||||
|
||||
stack<CFGNode*> nodesToCopy;
|
||||
nodesToCopy.push(_modifierFlow.entry);
|
||||
|
||||
// map the modifier entry to a new node that will become the new function entry
|
||||
copySrcToCopyDst[_modifierFlow.entry] = m_nodeContainer.newNode();
|
||||
|
||||
while (!nodesToCopy.empty())
|
||||
{
|
||||
CFGNode* copySrcNode = nodesToCopy.top();
|
||||
nodesToCopy.pop();
|
||||
|
||||
solAssert(copySrcToCopyDst.count(copySrcNode), "");
|
||||
|
||||
CFGNode* copyDstNode = copySrcToCopyDst[copySrcNode];
|
||||
|
||||
copyDstNode->block = copySrcNode->block;
|
||||
for (auto const& entry: copySrcNode->entries)
|
||||
{
|
||||
if (!copySrcToCopyDst.count(entry))
|
||||
{
|
||||
copySrcToCopyDst[entry] = m_nodeContainer.newNode();
|
||||
nodesToCopy.push(entry);
|
||||
}
|
||||
copyDstNode->entries.emplace_back(copySrcToCopyDst[entry]);
|
||||
}
|
||||
for (auto const& exit: copySrcNode->exits)
|
||||
{
|
||||
if (!copySrcToCopyDst.count(exit))
|
||||
{
|
||||
copySrcToCopyDst[exit] = m_nodeContainer.newNode();
|
||||
nodesToCopy.push(exit);
|
||||
}
|
||||
copyDstNode->exits.emplace_back(copySrcToCopyDst[exit]);
|
||||
}
|
||||
}
|
||||
|
||||
// if the modifier control flow never reached its exit node,
|
||||
// we need to create a new (disconnected) exit node now
|
||||
if (!copySrcToCopyDst.count(_modifierFlow.exit))
|
||||
copySrcToCopyDst[_modifierFlow.exit] = m_nodeContainer.newNode();
|
||||
|
||||
_functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
|
||||
_functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
|
||||
}
|
||||
|
@ -31,25 +31,57 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/** Basic Control Flow Block.
|
||||
* Basic block of control flow. Consists of a set of (unordered) AST nodes
|
||||
* for which control flow is always linear. A basic control flow block
|
||||
* encompasses at most one scope. Reverts are considered to break the control
|
||||
* flow.
|
||||
* @todo Handle function calls correctly. So far function calls are not considered
|
||||
* to change the control flow.
|
||||
*/
|
||||
struct ControlFlowBlock
|
||||
/** Occurrence of a variable in a block of control flow.
|
||||
* Stores the declaration of the referenced variable, the
|
||||
* kind of the occurrence and possibly the node at which
|
||||
* it occurred.
|
||||
*/
|
||||
class VariableOccurrence
|
||||
{
|
||||
/// All variable declarations inside this control flow block.
|
||||
std::vector<VariableDeclaration const*> variableDeclarations;
|
||||
/// All expressions inside this control flow block (this includes all subexpressions!).
|
||||
std::vector<Expression const*> expressions;
|
||||
/// All inline assembly statements inside in this control flow block.
|
||||
std::vector<InlineAssembly const*> inlineAssemblyStatements;
|
||||
/// If control flow returns in this node, the return statement is stored in returnStatement,
|
||||
/// otherwise returnStatement is nullptr.
|
||||
Return const* returnStatement = nullptr;
|
||||
public:
|
||||
enum class Kind
|
||||
{
|
||||
Declaration,
|
||||
Access,
|
||||
Return,
|
||||
Assignment,
|
||||
InlineAssembly
|
||||
};
|
||||
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.
|
||||
@ -64,14 +96,15 @@ struct CFGNode
|
||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||
std::vector<CFGNode*> exits;
|
||||
|
||||
/// Control flow in the node.
|
||||
ControlFlowBlock block;
|
||||
/// Variable occurrences in the node.
|
||||
std::vector<VariableOccurrence> variableOccurrences;
|
||||
};
|
||||
|
||||
/** Describes the control flow of a function. */
|
||||
struct FunctionFlow
|
||||
{
|
||||
virtual ~FunctionFlow() {}
|
||||
virtual ~FunctionFlow() = default;
|
||||
|
||||
/// Entry node. Control flow of the function starts here.
|
||||
/// This node is empty and does not have any entries.
|
||||
CFGNode* entry = nullptr;
|
||||
@ -85,19 +118,6 @@ struct FunctionFlow
|
||||
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
|
||||
{
|
||||
public:
|
||||
@ -105,7 +125,6 @@ public:
|
||||
|
||||
bool constructFlow(ASTNode const& _astRoot);
|
||||
|
||||
bool visit(ModifierDefinition const& _modifier) override;
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
|
||||
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
|
||||
@ -118,20 +137,6 @@ public:
|
||||
std::vector<std::unique_ptr<CFGNode>> m_nodes;
|
||||
};
|
||||
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;
|
||||
|
||||
@ -141,7 +146,6 @@ private:
|
||||
NodeContainer m_nodeContainer;
|
||||
|
||||
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
|
||||
std::map<ModifierDefinition const*, std::unique_ptr<ModifierFlow>> m_modifierControlFlow;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
@ -22,11 +22,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
@ -22,9 +22,10 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolidity/parsing/DocStringParser.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
@ -21,10 +21,11 @@
|
||||
* Container of the (implicit and explicit) global objects.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <libsolidity/analysis/GlobalContext.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -22,12 +22,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
@ -22,11 +22,10 @@
|
||||
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
@ -22,13 +22,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
|
@ -16,15 +16,14 @@
|
||||
*/
|
||||
|
||||
#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 <boost/range/adaptor/map.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
@ -21,12 +21,15 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.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.
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/SemVerHandler.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/StaticAnalyzer.h>
|
||||
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
@ -63,21 +64,21 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
|
||||
|
||||
void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
||||
{
|
||||
m_currentFunction = nullptr;
|
||||
m_constructor = false;
|
||||
for (auto const& var: m_localVarUseCount)
|
||||
if (var.second == 0)
|
||||
{
|
||||
if (var.first.second->isCallableParameter())
|
||||
m_errorReporter.warning(
|
||||
var.first.second->location(),
|
||||
"Unused function parameter. Remove or comment out the variable name to silence this warning."
|
||||
);
|
||||
else
|
||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||
}
|
||||
|
||||
if (m_currentFunction && !m_currentFunction->body().statements().empty())
|
||||
for (auto const& var: m_localVarUseCount)
|
||||
if (var.second == 0)
|
||||
{
|
||||
if (var.first.second->isCallableParameter())
|
||||
m_errorReporter.warning(
|
||||
var.first.second->location(),
|
||||
"Unused function parameter. Remove or comment out the variable name to silence this warning."
|
||||
);
|
||||
else
|
||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||
}
|
||||
m_localVarUseCount.clear();
|
||||
m_constructor = false;
|
||||
m_currentFunction = nullptr;
|
||||
}
|
||||
|
||||
bool StaticAnalyzer::visit(Identifier const& _identifier)
|
||||
|
@ -16,15 +16,18 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||
#include <memory>
|
||||
|
||||
#include <libsolidity/analysis/SemVerHandler.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||
#include <libsolidity/analysis/SemVerHandler.h>
|
||||
#include <liblangutil/ErrorReporter.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 <memory>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
@ -111,7 +114,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||
SemVerMatchExpressionParser parser(tokens, literals);
|
||||
auto matchExpression = parser.parse();
|
||||
SemVerVersion currentVersion{string(VersionString)};
|
||||
static SemVerVersion const currentVersion{string(VersionString)};
|
||||
if (!matchExpression.matches(currentVersion))
|
||||
m_errorReporter.syntaxError(
|
||||
_pragma.location(),
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@ -33,8 +34,8 @@
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -658,7 +659,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
m_errorReporter,
|
||||
m_evmVersion,
|
||||
Error::Type::SyntaxError,
|
||||
yul::AsmFlavour::Loose,
|
||||
yul::EVMDialect::looseAssemblyForEVM(),
|
||||
identifierAccess
|
||||
);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
@ -935,30 +936,32 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
var.accept(*this);
|
||||
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
|
||||
{
|
||||
auto errorMsg = "Type " +
|
||||
valueComponentType->toString() +
|
||||
" is not implicitly convertible to expected type " +
|
||||
var.annotation().type->toString();
|
||||
if (
|
||||
valueComponentType->category() == Type::Category::RationalNumber &&
|
||||
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
|
||||
valueComponentType->mobileType()
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
_statement.location(),
|
||||
"Type " +
|
||||
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."
|
||||
);
|
||||
{
|
||||
if (var.annotation().type->operator==(*valueComponentType->mobileType()))
|
||||
m_errorReporter.typeError(
|
||||
_statement.location(),
|
||||
errorMsg + ", but it can be explicitly converted."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
_statement.location(),
|
||||
errorMsg +
|
||||
". Try converting to type " +
|
||||
valueComponentType->mobileType()->toString() +
|
||||
" or use an explicit conversion."
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
_statement.location(),
|
||||
"Type " +
|
||||
valueComponentType->toString() +
|
||||
" is not implicitly convertible to expected type " +
|
||||
var.annotation().type->toString() +
|
||||
"."
|
||||
);
|
||||
m_errorReporter.typeError(_statement.location(), errorMsg + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1252,7 +1255,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
{
|
||||
TypePointer const& leftType = type(_operation.leftExpression());
|
||||
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)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
@ -1262,7 +1266,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
|
||||
" not compatible with types " +
|
||||
leftType->toString() +
|
||||
" and " +
|
||||
rightType->toString()
|
||||
rightType->toString() +
|
||||
(!result.message().empty() ? ". " + result.message() : "")
|
||||
);
|
||||
commonType = leftType;
|
||||
}
|
||||
@ -2329,30 +2334,32 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
||||
_expression.accept(*this);
|
||||
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
|
||||
{
|
||||
auto errorMsg = "Type " +
|
||||
type(_expression)->toString() +
|
||||
" is not implicitly convertible to expected type " +
|
||||
_expectedType.toString();
|
||||
if (
|
||||
type(_expression)->category() == Type::Category::RationalNumber &&
|
||||
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
|
||||
type(_expression)->mobileType()
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
_expression.location(),
|
||||
"Type " +
|
||||
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."
|
||||
);
|
||||
{
|
||||
if (_expectedType.operator==(*type(_expression)->mobileType()))
|
||||
m_errorReporter.typeError(
|
||||
_expression.location(),
|
||||
errorMsg + ", but it can be explicitly converted."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
_expression.location(),
|
||||
errorMsg +
|
||||
". Try converting to type " +
|
||||
type(_expression)->mobileType()->toString() +
|
||||
" or use an explicit conversion."
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
_expression.location(),
|
||||
"Type " +
|
||||
type(_expression)->toString() +
|
||||
" is not implicitly convertible to expected type " +
|
||||
_expectedType.toString() +
|
||||
"."
|
||||
);
|
||||
m_errorReporter.typeError(_expression.location(), errorMsg + ".");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -24,10 +24,10 @@
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
|
@ -16,14 +16,10 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/ViewPureChecker.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
@ -156,6 +152,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
||||
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
|
||||
_funDef.stateMutability() != StateMutability::Payable &&
|
||||
_funDef.isImplemented() &&
|
||||
!_funDef.body().statements().empty() &&
|
||||
!_funDef.isConstructor() &&
|
||||
!_funDef.isFallback() &&
|
||||
!_funDef.annotation().superFunction
|
||||
|
@ -21,13 +21,12 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/AST_accept.h>
|
||||
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
@ -198,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
|
||||
{
|
||||
signaturesSeen.insert(functionSignature);
|
||||
FixedHash<4> hash(dev::keccak256(functionSignature));
|
||||
m_interfaceFunctionList->push_back(make_pair(hash, fun));
|
||||
m_interfaceFunctionList->emplace_back(hash, fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,24 +22,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <libsolidity/parsing/Token.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <libsolidity/ast/ASTEnums.h>
|
||||
#include <libsolidity/parsing/Token.h>
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
|
||||
#include <libdevcore/FixedHash.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
|
@ -27,8 +27,8 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace yul
|
||||
{
|
||||
@ -46,7 +46,7 @@ using TypePointer = std::shared_ptr<Type const>;
|
||||
|
||||
struct ASTAnnotation
|
||||
{
|
||||
virtual ~ASTAnnotation() {}
|
||||
virtual ~ASTAnnotation() = default;
|
||||
};
|
||||
|
||||
struct DocTag
|
||||
@ -57,7 +57,7 @@ struct DocTag
|
||||
|
||||
struct DocumentedAnnotation
|
||||
{
|
||||
virtual ~DocumentedAnnotation() {}
|
||||
virtual ~DocumentedAnnotation() = default;
|
||||
/// Mapping docstring tag name -> content.
|
||||
std::multimap<std::string, DocTag> docTags;
|
||||
};
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward-declare all AST node types
|
||||
|
@ -20,11 +20,12 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/ast/ASTJsonConverter.h>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <libdevcore/UTF8.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libdevcore/UTF8.h>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
|
||||
using namespace std;
|
||||
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("scope", idOrNull(_node.scope()))
|
||||
};
|
||||
attributes.push_back(make_pair("unitAlias", _node.name()));
|
||||
attributes.emplace_back("unitAlias", _node.name());
|
||||
Json::Value symbolAliases(Json::arrayValue);
|
||||
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;
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
@ -357,7 +358,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
|
||||
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
|
||||
};
|
||||
if (m_inEvent)
|
||||
attributes.push_back(make_pair("indexed", _node.isIndexed()));
|
||||
attributes.emplace_back("indexed", _node.isIndexed());
|
||||
setJsonNode(_node, "VariableDeclaration", std::move(attributes));
|
||||
return false;
|
||||
}
|
||||
@ -647,11 +648,11 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
|
||||
};
|
||||
if (m_legacy)
|
||||
{
|
||||
attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall));
|
||||
attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion));
|
||||
attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
|
||||
attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
|
||||
}
|
||||
else
|
||||
attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind)));
|
||||
attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
|
||||
appendExpressionAttributes(attributes, _node.annotation());
|
||||
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
||||
return false;
|
||||
@ -724,7 +725,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
|
||||
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(
|
||||
"subdenomination",
|
||||
subdenomination == Token::Illegal ?
|
||||
|
@ -22,12 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
|
||||
#include <json/json.h>
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
|
||||
namespace langutil
|
||||
{
|
||||
|
@ -21,11 +21,10 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/ast/ASTPrinter.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <json/json.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace langutil;
|
||||
|
@ -22,9 +22,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/interface/GasEstimator.h>
|
||||
#include <ostream>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
@ -22,10 +22,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
@ -24,22 +24,22 @@
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <libdevcore/CommonIO.h>
|
||||
#include <libdevcore/Algorithms.h>
|
||||
#include <libdevcore/CommonData.h>
|
||||
#include <libdevcore/CommonIO.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
#include <libdevcore/UTF8.h>
|
||||
#include <libdevcore/Algorithms.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string.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/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
#include <boost/range/adaptor/sliced.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
@ -125,6 +125,22 @@ bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _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)
|
||||
@ -446,7 +462,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
|
||||
continue;
|
||||
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
|
||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||
members.push_back(MemberList::Member(function->name(), fun, function));
|
||||
members.emplace_back(function->name(), fun, function);
|
||||
}
|
||||
}
|
||||
return members;
|
||||
@ -466,7 +482,7 @@ string AddressType::richIdentifier() const
|
||||
return "t_address";
|
||||
}
|
||||
|
||||
bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||
BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||
{
|
||||
if (_other.category() != category())
|
||||
return false;
|
||||
@ -475,7 +491,7 @@ bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||
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))
|
||||
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
|
||||
@ -504,17 +520,16 @@ u256 AddressType::literalValue(Literal const* _literal) const
|
||||
return u256(_literal->valueWithoutUnderscores());
|
||||
}
|
||||
|
||||
TypePointer AddressType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult AddressType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
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))
|
||||
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);
|
||||
}
|
||||
@ -576,7 +591,7 @@ string IntegerType::richIdentifier() const
|
||||
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())
|
||||
{
|
||||
@ -597,7 +612,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
return _convertTo.category() == category() ||
|
||||
_convertTo.category() == Category::Address ||
|
||||
@ -607,18 +622,17 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
_convertTo.category() == Category::FixedPoint;
|
||||
}
|
||||
|
||||
TypePointer IntegerType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult IntegerType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
// "delete" is ok for all integer types
|
||||
if (_operator == Token::Delete)
|
||||
return make_shared<TupleType>();
|
||||
// we allow +, -, ++ and --
|
||||
else if (_operator == Token::Add || _operator == Token::Sub ||
|
||||
_operator == Token::Inc || _operator == Token::Dec ||
|
||||
_operator == Token::BitNot)
|
||||
return shared_from_this();
|
||||
return TypeResult{make_shared<TupleType>()};
|
||||
// we allow -, ++ and --
|
||||
else if (_operator == Token::Sub || _operator == Token::Inc ||
|
||||
_operator == Token::Dec || _operator == Token::BitNot)
|
||||
return TypeResult{shared_from_this()};
|
||||
else
|
||||
return TypePointer();
|
||||
return TypeResult{""};
|
||||
}
|
||||
|
||||
bool IntegerType::operator==(Type const& _other) const
|
||||
@ -651,7 +665,7 @@ bigint IntegerType::maxValue() const
|
||||
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 (
|
||||
_other->category() != Category::RationalNumber &&
|
||||
@ -679,9 +693,8 @@ TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const
|
||||
return TypePointer();
|
||||
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
|
||||
{
|
||||
// Signed EXP is not allowed
|
||||
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))
|
||||
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);
|
||||
}
|
||||
|
||||
bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (_convertTo.category() == category())
|
||||
{
|
||||
@ -717,18 +730,18 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
|
||||
}
|
||||
|
||||
TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult FixedPointType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
switch(_operator)
|
||||
{
|
||||
case Token::Delete:
|
||||
// "delete" is ok for all fixed types
|
||||
return make_shared<TupleType>();
|
||||
return TypeResult(make_shared<TupleType>());
|
||||
case Token::Add:
|
||||
case Token::Sub:
|
||||
case Token::Inc:
|
||||
@ -771,7 +784,7 @@ bigint FixedPointType::minIntegerValue() const
|
||||
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);
|
||||
|
||||
@ -957,7 +970,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
||||
return make_tuple(true, value);
|
||||
}
|
||||
|
||||
bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
switch (_convertTo.category())
|
||||
{
|
||||
@ -966,27 +979,21 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
if (isFractional())
|
||||
return false;
|
||||
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
|
||||
if (m_value == rational(0))
|
||||
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;
|
||||
return fitsIntegerType(m_value.numerator(), targetType);
|
||||
}
|
||||
case Category::FixedPoint:
|
||||
{
|
||||
if (auto fixed = fixedPointType())
|
||||
return fixed->isImplicitlyConvertibleTo(_convertTo);
|
||||
return false;
|
||||
FixedPointType const& targetType = dynamic_cast<FixedPointType const&>(_convertTo);
|
||||
// Store a negative number into an unsigned.
|
||||
if (isNegative() && !targetType.isSigned())
|
||||
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:
|
||||
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))
|
||||
return true;
|
||||
@ -1008,7 +1015,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
return false;
|
||||
}
|
||||
|
||||
TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
rational value;
|
||||
switch (_operator)
|
||||
@ -1029,10 +1036,10 @@ TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
|
||||
default:
|
||||
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)
|
||||
{
|
||||
@ -1129,9 +1136,8 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
|
||||
|
||||
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))
|
||||
return TypePointer();
|
||||
return TypeResult{"Precision of rational constants is limited to 4096 bits."};
|
||||
|
||||
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
|
||||
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
|
||||
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))
|
||||
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())
|
||||
return false;
|
||||
@ -1417,7 +1423,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
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()) ||
|
||||
(_convertTo.category() == Category::Address && numBytes() == 20) ||
|
||||
@ -1425,18 +1431,18 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
_convertTo.category() == category();
|
||||
}
|
||||
|
||||
TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
// "delete" and "~" is okay for FixedBytesType
|
||||
if (_operator == Token::Delete)
|
||||
return make_shared<TupleType>();
|
||||
return TypeResult(make_shared<TupleType>());
|
||||
else if (_operator == Token::BitNot)
|
||||
return shared_from_this();
|
||||
|
||||
return TypePointer();
|
||||
}
|
||||
|
||||
TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||
TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
|
||||
{
|
||||
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
|
||||
if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
|
||||
return commonType;
|
||||
return TypeResult(commonType);
|
||||
|
||||
return TypePointer();
|
||||
}
|
||||
@ -1486,14 +1492,14 @@ u256 BoolType::literalValue(Literal const* _literal) const
|
||||
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)
|
||||
return make_shared<TupleType>();
|
||||
return TypeResult(make_shared<TupleType>());
|
||||
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())
|
||||
return TypePointer();
|
||||
@ -1503,7 +1509,7 @@ TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _
|
||||
return TypePointer();
|
||||
}
|
||||
|
||||
bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (*this == _convertTo)
|
||||
return true;
|
||||
@ -1520,7 +1526,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
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))
|
||||
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
|
||||
@ -1533,14 +1539,14 @@ bool ContractType::isPayable() const
|
||||
return fallbackFunction && fallbackFunction->isPayable();
|
||||
}
|
||||
|
||||
TypePointer ContractType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult ContractType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
if (isSuper())
|
||||
return 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)
|
||||
return TypePointer();
|
||||
@ -1551,7 +1557,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
|
||||
case DataLocation::CallData:
|
||||
return TypePointer();
|
||||
case DataLocation::Memory:
|
||||
return make_shared<TupleType>();
|
||||
return TypeResult(make_shared<TupleType>());
|
||||
case DataLocation::Storage:
|
||||
return m_isPointer ? TypePointer() : make_shared<TupleType>();
|
||||
}
|
||||
@ -1605,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const
|
||||
return id;
|
||||
}
|
||||
|
||||
bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||
BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||
{
|
||||
if (_convertTo.category() != category())
|
||||
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))
|
||||
return true;
|
||||
@ -1815,23 +1821,23 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap members;
|
||||
if (!isString())
|
||||
{
|
||||
members.push_back({"length", make_shared<IntegerType>(256)});
|
||||
members.emplace_back("length", make_shared<IntegerType>(256));
|
||||
if (isDynamicallySized() && location() == DataLocation::Storage)
|
||||
{
|
||||
members.push_back({"push", make_shared<FunctionType>(
|
||||
members.emplace_back("push", make_shared<FunctionType>(
|
||||
TypePointers{baseType()},
|
||||
TypePointers{make_shared<IntegerType>(256)},
|
||||
strings{string()},
|
||||
strings{string()},
|
||||
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
|
||||
)});
|
||||
members.push_back({"pop", make_shared<FunctionType>(
|
||||
));
|
||||
members.emplace_back("pop", make_shared<FunctionType>(
|
||||
TypePointers{},
|
||||
TypePointers{},
|
||||
strings{string()},
|
||||
strings{string()},
|
||||
FunctionType::Kind::ArrayPop
|
||||
)});
|
||||
));
|
||||
}
|
||||
}
|
||||
return members;
|
||||
@ -1960,21 +1966,17 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
|
||||
break;
|
||||
}
|
||||
if (!functionWithEqualArgumentsFound)
|
||||
members.push_back(MemberList::Member(
|
||||
function->name(),
|
||||
functionType,
|
||||
function
|
||||
));
|
||||
members.emplace_back(function->name(), functionType, function);
|
||||
}
|
||||
}
|
||||
else if (!m_contract.isLibrary())
|
||||
{
|
||||
for (auto const& it: m_contract.interfaceFunctions())
|
||||
members.push_back(MemberList::Member(
|
||||
members.emplace_back(
|
||||
it.second->declaration().name(),
|
||||
it.second->asCallableFunction(m_contract.isLibrary()),
|
||||
&it.second->declaration()
|
||||
));
|
||||
);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
@ -2002,11 +2004,11 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
|
||||
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
|
||||
for (size_t index = 0; index < variables.size(); ++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;
|
||||
}
|
||||
|
||||
bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||
BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||
{
|
||||
if (_convertTo.category() != category())
|
||||
return false;
|
||||
@ -2092,10 +2094,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
||||
// Skip all mapping members if we are not in storage.
|
||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
||||
continue;
|
||||
members.push_back(MemberList::Member(
|
||||
members.emplace_back(
|
||||
variable->name(),
|
||||
copyForLocationIfReference(type),
|
||||
variable.get())
|
||||
variable.get()
|
||||
);
|
||||
}
|
||||
return members;
|
||||
@ -2249,7 +2251,7 @@ bool StructType::recursive() const
|
||||
return *m_recursive;
|
||||
}
|
||||
|
||||
TypePointer EnumType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult EnumType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
|
||||
}
|
||||
@ -2291,7 +2293,7 @@ size_t EnumType::numberOfMembers() const
|
||||
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;
|
||||
}
|
||||
@ -2308,7 +2310,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const
|
||||
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))
|
||||
{
|
||||
@ -2432,7 +2434,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
||||
{
|
||||
m_parameterTypes.push_back(mappingType->keyType());
|
||||
m_parameterNames.push_back("");
|
||||
m_parameterNames.emplace_back("");
|
||||
returnType = mappingType->valueType();
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
|
||||
@ -2441,7 +2443,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
// Return byte arrays as whole.
|
||||
break;
|
||||
returnType = arrayType->baseType();
|
||||
m_parameterNames.push_back("");
|
||||
m_parameterNames.emplace_back("");
|
||||
m_parameterTypes.push_back(make_shared<IntegerType>(256));
|
||||
}
|
||||
else
|
||||
@ -2472,7 +2474,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
DataLocation::Memory,
|
||||
returnType
|
||||
));
|
||||
m_returnParameterNames.push_back("");
|
||||
m_returnParameterNames.emplace_back("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2648,14 +2650,14 @@ bool FunctionType::operator==(Type const& _other) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (m_kind == Kind::External && _convertTo == AddressType::address())
|
||||
return true;
|
||||
return _convertTo.category() == category();
|
||||
}
|
||||
|
||||
bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
if (_convertTo.category() != category())
|
||||
return false;
|
||||
@ -2680,14 +2682,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
return true;
|
||||
}
|
||||
|
||||
TypePointer FunctionType::unaryOperatorResult(Token _operator) const
|
||||
TypeResult FunctionType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
if (_operator == Token::Delete)
|
||||
return make_shared<TupleType>();
|
||||
return TypeResult(make_shared<TupleType>());
|
||||
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))
|
||||
return TypePointer();
|
||||
@ -2841,14 +2843,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
if (m_kind == Kind::External)
|
||||
members.push_back(MemberList::Member(
|
||||
"selector",
|
||||
make_shared<FixedBytesType>(4)
|
||||
));
|
||||
members.emplace_back("selector", make_shared<FixedBytesType>(4));
|
||||
if (m_kind != Kind::BareDelegateCall)
|
||||
{
|
||||
if (isPayable())
|
||||
members.push_back(MemberList::Member(
|
||||
members.emplace_back(
|
||||
"value",
|
||||
make_shared<FunctionType>(
|
||||
parseElementaryTypeVector({"uint"}),
|
||||
@ -2862,10 +2861,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
||||
m_gasSet,
|
||||
m_valueSet
|
||||
)
|
||||
));
|
||||
);
|
||||
}
|
||||
if (m_kind != Kind::Creation)
|
||||
members.push_back(MemberList::Member(
|
||||
members.emplace_back(
|
||||
"gas",
|
||||
make_shared<FunctionType>(
|
||||
parseElementaryTypeVector({"uint"}),
|
||||
@ -2879,7 +2878,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
||||
m_gasSet,
|
||||
m_valueSet
|
||||
)
|
||||
));
|
||||
);
|
||||
return members;
|
||||
}
|
||||
default:
|
||||
@ -3207,24 +3206,24 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
||||
if (contract.isLibrary())
|
||||
for (FunctionDefinition const* function: contract.definedFunctions())
|
||||
if (function->isVisibleAsLibraryMember())
|
||||
members.push_back(MemberList::Member(
|
||||
members.emplace_back(
|
||||
function->name(),
|
||||
FunctionType(*function).asCallableFunction(true),
|
||||
function
|
||||
));
|
||||
);
|
||||
if (isBase)
|
||||
{
|
||||
// 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.
|
||||
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
|
||||
{
|
||||
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())
|
||||
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)
|
||||
@ -3232,7 +3231,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
|
||||
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
|
||||
auto enumType = make_shared<EnumType>(enumDef);
|
||||
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
|
||||
members.push_back(MemberList::Member(enumValue->name(), enumType));
|
||||
members.emplace_back(enumValue->name(), enumType);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
@ -3297,7 +3296,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap symbols;
|
||||
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -22,22 +22,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/ASTEnums.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/parsing/Token.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/CommonIO.h>
|
||||
#include <libdevcore/Result.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -50,6 +51,8 @@ using TypePointer = std::shared_ptr<Type const>;
|
||||
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
||||
using TypePointers = std::vector<TypePointer>;
|
||||
using rational = boost::rational<dev::bigint>;
|
||||
using TypeResult = Result<TypePointer>;
|
||||
using BoolResult = Result<bool>;
|
||||
|
||||
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 };
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static std::string escapeIdentifier(std::string const& _identifier);
|
||||
|
||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
virtual BoolResult isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||
virtual BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
{
|
||||
return isImplicitlyConvertibleTo(_convertTo);
|
||||
}
|
||||
/// @returns the resulting type of applying the given unary operator or an empty pointer if
|
||||
/// this is not possible.
|
||||
/// 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
|
||||
/// this is not possible.
|
||||
/// 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();
|
||||
}
|
||||
@ -336,10 +340,10 @@ public:
|
||||
explicit AddressType(StateMutability _stateMutability);
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
|
||||
bool operator==(Type const& _other) const override;
|
||||
|
||||
@ -381,10 +385,10 @@ public:
|
||||
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token _operator, TypePointer 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);
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token _operator, TypePointer 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()):
|
||||
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
|
||||
{}
|
||||
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
@ -536,8 +539,8 @@ public:
|
||||
|
||||
explicit StringLiteralType(Literal const& _literal);
|
||||
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||
{
|
||||
return TypePointer();
|
||||
}
|
||||
@ -570,12 +573,12 @@ public:
|
||||
|
||||
explicit FixedBytesType(unsigned _bytes);
|
||||
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) 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 storageBytes() const override { return m_bytes; }
|
||||
@ -598,11 +601,10 @@ private:
|
||||
class BoolType: public Type
|
||||
{
|
||||
public:
|
||||
BoolType() {}
|
||||
Category category() const override { return Category::Bool; }
|
||||
std::string richIdentifier() const override { return "t_bool"; }
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
|
||||
|
||||
unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
|
||||
unsigned storageBytes() const override { return 1; }
|
||||
@ -624,8 +626,8 @@ public:
|
||||
explicit ReferenceType(DataLocation _location): m_location(_location) {}
|
||||
DataLocation location() const { return m_location; }
|
||||
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||
{
|
||||
return TypePointer();
|
||||
}
|
||||
@ -702,8 +704,8 @@ public:
|
||||
m_length(_length)
|
||||
{}
|
||||
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(const Type& _other) const override;
|
||||
unsigned calldataEncodedSize(bool _padded) const override;
|
||||
@ -757,10 +759,10 @@ public:
|
||||
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
|
||||
m_contract(_contract), m_super(_super) {}
|
||||
/// 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.
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
unsigned calldataEncodedSize(bool _padded ) const override
|
||||
@ -821,7 +823,7 @@ public:
|
||||
Category category() const override { return Category::Struct; }
|
||||
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
|
||||
ReferenceType(_location), m_struct(_struct) {}
|
||||
bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
unsigned calldataEncodedSize(bool _padded) const override;
|
||||
@ -876,7 +878,7 @@ class EnumType: public Type
|
||||
public:
|
||||
Category category() const override { return Category::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;
|
||||
bool operator==(Type const& _other) const override;
|
||||
unsigned calldataEncodedSize(bool _padded) const override
|
||||
@ -889,7 +891,7 @@ public:
|
||||
std::string canonicalName() const override;
|
||||
bool isValueType() const override { return true; }
|
||||
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer encodingType() const override
|
||||
{
|
||||
return std::make_shared<IntegerType>(8 * int(storageBytes()));
|
||||
@ -917,10 +919,10 @@ class TupleType: public Type
|
||||
public:
|
||||
Category category() const override { return Category::Tuple; }
|
||||
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;
|
||||
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;
|
||||
bool canBeStored() const override { return false; }
|
||||
u256 storageSize() const override;
|
||||
@ -1065,10 +1067,10 @@ public:
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypePointer unaryOperatorResult(Token _operator) const override;
|
||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
|
||||
BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
TypeResult unaryOperatorResult(Token _operator) const override;
|
||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override;
|
||||
std::string canonicalName() const override;
|
||||
std::string toString(bool _short) const override;
|
||||
unsigned calldataEncodedSize(bool _padded) const override;
|
||||
@ -1197,7 +1199,7 @@ public:
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
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
|
||||
{
|
||||
return std::make_shared<IntegerType>(256);
|
||||
@ -1230,7 +1232,7 @@ public:
|
||||
explicit TypeType(TypePointer const& _actualType): m_actualType(_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;
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
@ -1255,7 +1257,7 @@ public:
|
||||
Category category() const override { return Category::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; }
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
@ -1281,7 +1283,7 @@ public:
|
||||
|
||||
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;
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
@ -1308,7 +1310,7 @@ public:
|
||||
|
||||
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
||||
|
||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override
|
||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||
{
|
||||
return TypePointer();
|
||||
}
|
||||
@ -1339,9 +1341,9 @@ public:
|
||||
Category category() const override { return Category::InaccessibleDynamic; }
|
||||
|
||||
std::string richIdentifier() const override { return "t_inaccessible"; }
|
||||
bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||
bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||
TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||
BoolResult isImplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||
BoolResult isExplicitlyConvertibleTo(Type const&) const override { return false; }
|
||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
|
||||
unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
|
||||
#include <libdevcore/Whiskers.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@ -141,8 +140,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
vector<string> valueNamesLocal;
|
||||
for (size_t j = 0; j < sizeOnStack; j++)
|
||||
{
|
||||
valueNamesLocal.push_back("value" + to_string(stackPos));
|
||||
valueReturnParams.push_back("value" + to_string(stackPos));
|
||||
valueNamesLocal.emplace_back("value" + to_string(stackPos));
|
||||
valueReturnParams.emplace_back("value" + to_string(stackPos));
|
||||
stackPos++;
|
||||
}
|
||||
bool dynamic = decodingTypes[i]->isDynamicallyEncoded();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user