Merge pull request #5697 from ethereum/develop

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

View File

@ -15,7 +15,7 @@ defaults:
mkdir -p build
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:

View File

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

View File

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

View File

@ -1,3 +1,32 @@
### 0.5.2 (2018-12-19)
Language Features:
* Control Flow Graph: Detect every access to uninitialized storage pointers.
Compiler Features:
* Inline Assembly: Improve error messages around invalid function argument count.
* Code Generator: Only check callvalue once if all functions are non-payable.
* Code Generator: Use codecopy for string constants more aggressively.
* Code Generator: Use binary search for dispatch function if more efficient. The size/speed tradeoff can be tuned using ``--optimize-runs``.
* SMTChecker: Support mathematical and cryptographic functions in an uninterpreted way.
* SMTChecker: Support one-dimensional mappings.
* Standard JSON Interface: Disallow unknown keys in standard JSON input.
* Standard JSON Interface: Only run code generation if it has been requested. This could lead to unsupported feature errors only being reported at the point where you request bytecode.
* Static Analyzer: Do not warn about unused variables or state mutability for functions with an empty body.
* Type Checker: Add an additional reason to be displayed when type conversion fails.
* Yul: Support object access via ``datasize``, ``dataoffset`` and ``datacopy`` in standalone assembly mode.
Bugfixes:
* Standard JSON Interface: Report specific error message for json input errors instead of internal compiler error.
Build System:
* Replace the trusty PPA build by a static build on cosmic that is used for the trusty package instead.
* Remove support for Visual Studio 2015.
### 0.5.1 (2018-12-03)
Language Features:

View File

@ -10,6 +10,7 @@ Checklist for making a release:
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
- [ ] 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.

View File

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

View File

@ -25,8 +25,8 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
# 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -159,8 +159,8 @@ following list can be used as a reference of its opcodes.
If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
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.

View File

@ -616,5 +616,9 @@
"0.5.1": {
"bugs": [],
"released": "2018-12-03"
},
"0.5.2": {
"bugs": [],
"released": "2018-12-19"
}
}

View File

@ -1408,7 +1408,7 @@ Interfaces are denoted by their own keyword:
::
pragma solidity >=0.4.11 <0.6.0;
pragma solidity >=0.5.0 <0.6.0;
interface Token {
enum TokenType { Fungible, NonFungible }

View File

@ -61,7 +61,7 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please
follow the style of previous entries, when applicable.
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
--------------------------------

View File

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

View File

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

View File

@ -41,7 +41,7 @@ source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
A contract in the sense of Solidity is a collection of code (its *functions*) and
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

View File

@ -267,7 +267,7 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible
(these are NEL, LS and PS), it will lead to a parser error.
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

View File

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

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

View File

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

View File

@ -19,6 +19,7 @@ on its type. To handle any unexpected values, you should use the :ref:`revert fu
tuple with a second `bool` value denoting success.
.. 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``.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -0,0 +1,66 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
namespace dev
{
/// Simple generic result that holds a value and an optional error message.
/// Results can be implicitly converted to and created from the type of
/// the value they hold. The class is mainly designed for a result type of
/// bool or pointer type. The idea is that the default constructed value of
/// the result type is interpreted as an error value.
///
/// Result<bool> check()
/// {
/// if (false)
/// return Result<bool>("Error message.")
/// return true;
/// }
///
template <class ResultType>
class Result
{
public:
Result(ResultType _value): Result(_value, std::string{}) {}
Result(std::string _message): Result(ResultType{}, std::move(_message)) {}
/// @{
/// @name Wrapper functions
/// Wrapper functions that provide implicit conversions to and explicit retrieval of
/// the value this result holds.
operator ResultType const&() const { return m_value; }
ResultType const& get() const { return m_value; }
/// @}
/// @returns the error message (can be empty).
std::string const& message() const { return m_message; }
private:
explicit Result(ResultType _value, std::string _message):
m_value(std::move(_value)),
m_message(std::move(_message))
{}
ResultType m_value;
std::string m_message;
};
}

View File

@ -91,7 +91,7 @@ string dev::quotedAlternativesList(vector<string> const& suggestions)
vector<string> quotedSuggestions;
for (auto& suggestion: suggestions)
quotedSuggestions.push_back("\"" + suggestion + "\"");
quotedSuggestions.emplace_back("\"" + suggestion + "\"");
return joinHumanReadable(quotedSuggestions, ", ", " or ");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,89 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
#include <cmath>
#include <iomanip>
using namespace std;
using namespace dev;
using namespace langutil;
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
{
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
string const* message = boost::get_error_info<errinfo_comment>(_exception);
SourceReference primary = extract(location, message ? *message : "");
std::vector<SourceReference> secondary;
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
if (secondaryLocation && !secondaryLocation->infos.empty())
for (auto const& info: secondaryLocation->infos)
secondary.emplace_back(extract(&info.second, info.first));
return Message{std::move(primary), _category, std::move(secondary)};
}
SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message)
{
if (!_location || !_location->source.get()) // Nothing we can extract here
return SourceReference::MessageOnly(std::move(message));
shared_ptr<CharStream> const& source = _location->source;
LineColumn const interest = source->translatePositionToLineColumn(_location->start);
LineColumn start = interest;
LineColumn end = source->translatePositionToLineColumn(_location->end);
bool const isMultiline = start.line != end.line;
string line = source->lineAtPosition(_location->start);
int locationLength = isMultiline ? line.length() - start.column : end.column - start.column;
if (locationLength > 150)
{
line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35);
end.column = start.column + 75;
locationLength = 75;
}
if (line.length() > 150)
{
int const len = line.length();
line = line.substr(max(0, start.column - 35), min(start.column, 35) + min(locationLength + 35, len - start.column));
if (start.column + locationLength + 35 < len)
line += " ...";
if (start.column > 35)
{
line = " ... " + line;
start.column = 40;
}
end.column = start.column + locationLength;
}
return SourceReference{
std::move(message),
source->name(),
interest,
isMultiline,
line,
start.column,
end.column,
};
}

View File

@ -0,0 +1,75 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <iosfwd>
#include <string>
#include <tuple>
#include <vector>
namespace dev
{
struct Exception;
}
namespace langutil
{
struct LineColumn
{
int line = {-1};
int column = {-1};
LineColumn() = default;
LineColumn(std::tuple<int, int> const& _t): line{std::get<0>(_t)}, column{std::get<1>(_t)} {}
};
struct SourceReference
{
std::string message; ///< A message that relates to this source reference (such as a warning or an error message).
std::string sourceName; ///< Underlying source name (for example the filename).
LineColumn position; ///< Actual (error) position this source reference is surrounding.
bool multiline = {false}; ///< Indicates whether the actual SourceReference is truncated to one line.
std::string text; ///< Extracted source code text (potentially truncated if multiline or too long).
int startColumn = {-1}; ///< Highlighting range-start of text field.
int endColumn = {-1}; ///< Highlighting range-end of text field.
/// Constructs a SourceReference containing a message only.
static SourceReference MessageOnly(std::string _msg)
{
SourceReference sref;
sref.message = std::move(_msg);
return sref;
}
};
struct SourceLocation;
namespace SourceReferenceExtractor
{
struct Message
{
SourceReference primary;
std::string category; // "Error", "Warning", ...
std::vector<SourceReference> secondary;
};
Message extract(dev::Exception const& _exception, std::string _category);
SourceReference extract(SourceLocation const* _location, std::string message = "");
}
}

View File

@ -30,100 +30,63 @@ using namespace langutil;
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
{
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);
}
}

View File

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

View File

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

View File

@ -353,7 +353,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
if (j.tag() || j.which() != sp::utree_type::symbol_type)
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); };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,6 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
#include <map>
namespace langutil

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/DeclarationContainer.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
#include <libdevcore/StringUtils.h>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,8 +22,8 @@
#pragma once
#include <string>
#include <memory>
#include <string>
#include <vector>
// Forward-declare all AST node types

View File

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

View File

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

View File

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

View File

@ -22,9 +22,9 @@
#pragma once
#include <ostream>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/GasEstimator.h>
#include <ostream>
namespace dev
{

View File

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

View File

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

View File

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

View File

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